diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index afb5552192..baafb242af 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -265,6 +265,7 @@ PCCERT PCs pcwsz PEGI +PFM pfn pfxpath Pherson @@ -282,6 +283,7 @@ processthreads productcode pscustomobject pseudocode +PSHOST psobject ptstr publickey @@ -300,6 +302,7 @@ REFIID regexes REGSAM relativefilepath +remoting reparse restsource rgex diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/AssertWinGetPackageManagerCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/AssertWinGetPackageManagerCommand.cs new file mode 100644 index 0000000000..6cfd6cea9b --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Commands/AssertWinGetPackageManagerCommand.cs @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------------- +// +// 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.Helpers; + + /// + /// Assert-WinGetPackageManager. Verifies winget is installed properly. + /// + [Cmdlet( + VerbsLifecycle.Assert, + Constants.WinGetNouns.WinGetPackageManager, + DefaultParameterSetName = Constants.IntegrityVersionSet)] + public class AssertWinGetPackageManagerCommand : BaseIntegrityCommand + { + /// + /// Validates winget is installed correctly. If not, throws an exception + /// with the reason why, if any. + /// + protected override void ProcessRecord() + { + string expectedVersion = string.Empty; + if (this.ParameterSetName == Constants.IntegrityLatestSet) + { + var gitHubRelease = new GitHubRelease(); + expectedVersion = gitHubRelease.GetLatestVersionTagName(this.IncludePreRelease.ToBool()); + } + else + { + expectedVersion = this.Version; + } + + WinGetIntegrity.AssertWinGet(this, expectedVersion); + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseIntegrityCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseIntegrityCommand.cs new file mode 100644 index 0000000000..59cf7f971f --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Commands/Common/BaseIntegrityCommand.cs @@ -0,0 +1,41 @@ +// ----------------------------------------------------------------------------- +// +// 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 BaseIntegrityCommand : BaseCommand + { + /// + /// 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)] + public SwitchParameter IncludePreRelease { get; set; } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/GetVersionCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/GetVersionCommand.cs new file mode 100644 index 0000000000..5253713186 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Commands/GetVersionCommand.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------------- +// +// 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.Helpers; + + /// + /// Get-WinGetVersion. Gets the current version of winget. + /// + [Cmdlet(VerbsCommon.Get, Constants.WinGetNouns.Version)] + [OutputType(typeof(string))] + public class GetVersionCommand : BaseCommand + { + /// + /// Writes the winget version. + /// + protected override void ProcessRecord() + { + this.WriteObject(WinGetVersion.InstalledWinGetVersion.TagVersion); + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Commands/RepairWinGetPackageManagerCommand.cs b/src/PowerShell/Microsoft.WinGet.Client/Commands/RepairWinGetPackageManagerCommand.cs new file mode 100644 index 0000000000..22c2a8b3f5 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Commands/RepairWinGetPackageManagerCommand.cs @@ -0,0 +1,183 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Commands +{ + using System; + using System.Management.Automation; + using Microsoft.WinGet.Client.Commands.Common; + using Microsoft.WinGet.Client.Common; + using Microsoft.WinGet.Client.Helpers; + using Microsoft.WinGet.Client.Properties; + + /// + /// Repair-WinGetPackageManager. Repairs winget if needed. + /// + [Cmdlet( + VerbsDiagnostic.Repair, + Constants.WinGetNouns.WinGetPackageManager, + DefaultParameterSetName = Constants.IntegrityVersionSet)] + [OutputType(typeof(int))] + public class RepairWinGetPackageManagerCommand : BaseIntegrityCommand + { + private const string EnvPath = "env:PATH"; + private const int Succeeded = 0; + private const int Failed = -1; + + private static readonly string[] WriteInformationTags = new string[] { "PSHOST" }; + + /// + /// Attempts to repair winget. + /// TODO: consider WhatIf and Confirm options. + /// + protected override void ProcessRecord() + { + int result = Failed; + + string expectedVersion = this.Version; + if (this.ParameterSetName == Constants.IntegrityLatestSet) + { + var gitHubRelease = new GitHubRelease(); + expectedVersion = gitHubRelease.GetLatestVersionTagName(this.IncludePreRelease.ToBool()); + } + + var integrityCategory = WinGetIntegrity.GetIntegrityCategory(this, expectedVersion); + this.WriteDebug($"Integrity category type: {integrityCategory}"); + + if (integrityCategory == IntegrityCategory.Installed || + integrityCategory == IntegrityCategory.UnexpectedVersion) + { + result = this.VerifyWinGetInstall(integrityCategory, expectedVersion); + } + else if (integrityCategory == IntegrityCategory.NotInPath) + { + this.RepairEnvPath(); + + // Now try again and get the desired winget version if needed. + var newIntegrityCategory = WinGetIntegrity.GetIntegrityCategory(this, expectedVersion); + this.WriteDebug($"Integrity category after fixing PATH {newIntegrityCategory}"); + result = this.VerifyWinGetInstall(newIntegrityCategory, expectedVersion); + } + else if (integrityCategory == IntegrityCategory.AppInstallerNotRegistered) + { + var appxModule = new AppxModuleHelper(this); + appxModule.RegisterAppInstaller(); + + // Now try again and get the desired winget version if needed. + var newIntegrityCategory = WinGetIntegrity.GetIntegrityCategory(this, expectedVersion); + this.WriteDebug($"Integrity category after registering {newIntegrityCategory}"); + result = this.VerifyWinGetInstall(newIntegrityCategory, expectedVersion); + } + else if (integrityCategory == IntegrityCategory.AppInstallerNotInstalled || + integrityCategory == IntegrityCategory.AppInstallerNotSupported || + integrityCategory == IntegrityCategory.Failure) + { + // If we are here and expectedVersion 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(expectedVersion)) + { + var gitHubRelease = new GitHubRelease(); + expectedVersion = gitHubRelease.GetLatestVersionTagName(false); + } + + if (this.DownloadAndInstall(expectedVersion, false)) + { + result = Succeeded; + } + else + { + this.WriteDebug($"Failed installing {expectedVersion}"); + } + } + else if (integrityCategory == IntegrityCategory.AppExecutionAliasDisabled) + { + // Sorry, but the user has to manually enabled it. + this.WriteInformation(Resources.AppExecutionAliasDisabledHelpMessage, WriteInformationTags); + } + else + { + this.WriteInformation(Resources.WinGetNotSupportedMessage, WriteInformationTags); + } + + this.WriteObject(result); + } + + private int VerifyWinGetInstall(IntegrityCategory integrityCategory, string expectedVersion) + { + if (integrityCategory == IntegrityCategory.Installed) + { + // Nothing to do + this.WriteDebug($"WinGet is in a good state."); + return Succeeded; + } + else if (integrityCategory == IntegrityCategory.UnexpectedVersion) + { + // The versions are different, download and install. + if (!this.InstallDifferentVersion(new WinGetVersion(expectedVersion))) + { + this.WriteDebug($"Failed installing {expectedVersion}"); + } + else + { + return Succeeded; + } + } + + return Failed; + } + + private bool InstallDifferentVersion(WinGetVersion toInstallVersion) + { + var installedVersion = WinGetVersion.InstalledWinGetVersion; + + this.WriteDebug($"Installed WinGet version {installedVersion.TagVersion}"); + this.WriteDebug($"Installing WinGet version {toInstallVersion.TagVersion}"); + + bool downgrade = false; + if (installedVersion.CompareAsDeployment(toInstallVersion) > 0) + { + downgrade = true; + } + + return this.DownloadAndInstall(toInstallVersion.TagVersion, downgrade); + } + + private bool DownloadAndInstall(string versionTag, bool downgrade) + { + // Download and install. + var gitHubRelease = new GitHubRelease(); + var downloadedMsixBundlePath = gitHubRelease.DownloadRelease(versionTag); + + var appxModule = new AppxModuleHelper(this); + appxModule.AddAppInstallerBundle(downloadedMsixBundlePath, downgrade); + + // Verify that is installed + var integrityCategory = WinGetIntegrity.GetIntegrityCategory(this, versionTag); + if (integrityCategory != IntegrityCategory.Installed) + { + return false; + } + + this.WriteDebug($"Installed WinGet version {versionTag}"); + return true; + } + + 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.SessionState.PSVariable.Set(EnvPath, newPwshPathEnv); + + this.WriteDebug($"PATH environment variable updated"); + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/Constants.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/Constants.cs index a165353fa4..31f03f65f9 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Common/Constants.cs +++ b/src/PowerShell/Microsoft.WinGet.Client/Common/Constants.cs @@ -32,13 +32,51 @@ internal static class Constants /// 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"; - + public const string FoundSet = "FoundSet"; + + /// + /// 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"; + + /// + /// 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"; + /// /// 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"; + /// /// The noun analogue of the class. /// @@ -53,6 +91,11 @@ public static class WinGetNouns /// The noun for any user settings cmdlet. /// public const string UserSettings = "WinGetUserSettings"; + + /// + /// The noun for winget version. + /// + public const string Version = "WinGetVersion"; } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/IntegrityCategory.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/IntegrityCategory.cs new file mode 100644 index 0000000000..b6ebbd136e --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Common/IntegrityCategory.cs @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.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, + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs index 2fb27efe91..7b4de03367 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs +++ b/src/PowerShell/Microsoft.WinGet.Client/Common/Utilities.cs @@ -6,7 +6,7 @@ namespace Microsoft.WinGet.Client.Common { - using System.Resources; + using System; using System.Security.Principal; /// @@ -41,5 +41,43 @@ public static bool ExecutingAsSystem return principal.IsInRole(WindowsBuiltInRole.SystemOperator); } } + + /// + /// 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"); + } + } + + /// + /// 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); + } + } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Common/WinGetIntegrity.cs b/src/PowerShell/Microsoft.WinGet.Client/Common/WinGetIntegrity.cs new file mode 100644 index 0000000000..a5a8954cd9 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Common/WinGetIntegrity.cs @@ -0,0 +1,147 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Common +{ + using System; + using System.ComponentModel; + using System.IO; + using System.Management.Automation; + using Microsoft.WinGet.Client.Exceptions; + using Microsoft.WinGet.Client.Helpers; + using Microsoft.WinGet.Client.Properties; + + /// + /// 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(PSCmdlet psCmdlet, string expectedVersion) + { + try + { + // Start by calling winget without its WindowsApp PFN path. + // If it succeeds and the exit code is 0 then we are good. + var wingetCliWrapper = new WingetCLIWrapper(false); + var result = wingetCliWrapper.RunCommand("--version"); + result.VerifyExitCode(); + } + catch (Win32Exception) + { + throw new WinGetIntegrityException(GetReason(psCmdlet)); + } + catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) + { + throw new WinGetIntegrityException(IntegrityCategory.Failure, e); + } + catch (Exception e) + { + 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; + if (expectedWinGetVersion.CompareTo(installedVersion) != 0) + { + throw new WinGetIntegrityException( + IntegrityCategory.UnexpectedVersion, + string.Format( + Resources.IntegrityUnexpectedVersionMessage, + installedVersion, + expectedVersion)); + } + } + } + + /// + /// Verifies winget runs correctly. + /// + /// The calling cmdlet. + /// Expected version. + /// Integrity category. + public static IntegrityCategory GetIntegrityCategory(PSCmdlet psCmdlet, string expectedVersion) + { + try + { + AssertWinGet(psCmdlet, expectedVersion); + } + catch (WinGetIntegrityException e) + { + return e.Category; + } + + return IntegrityCategory.Installed; + } + + private static IntegrityCategory GetReason(PSCmdlet psCmdlet) + { + // Ok, so you are here because calling winget --version failed. Lets try to figure out why. + + // 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\PFM\ + + // 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; + if (osVersion.CompareTo(minWindowsVersion) < 0) + { + 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(psCmdlet); + 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; + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Crescendo/Crescendo.json b/src/PowerShell/Microsoft.WinGet.Client/Crescendo/Crescendo.json index 212d4e2e39..35186772f7 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Crescendo/Crescendo.json +++ b/src/PowerShell/Microsoft.WinGet.Client/Crescendo/Crescendo.json @@ -1,17 +1,6 @@ { "$schema": "https://aka.ms/PowerShell/Crescendo/Schemas/2021-11", "Commands": [ - { - "Verb": "Get", - "Noun": "WinGetVersion", - "Platform": [ - "Windows" - ], - "OriginalName": "winget.exe", - "OriginalCommandElements": [ - "--version" - ] - }, { "Verb": "Enable", "Noun": "WinGetSetting", diff --git a/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetCLITimeoutException.cs b/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetCLITimeoutException.cs new file mode 100644 index 0000000000..8ad5ff7e04 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetCLITimeoutException.cs @@ -0,0 +1,27 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Exceptions +{ + using System; + using Microsoft.WinGet.Client.Properties; + + /// + /// 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)) + { + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetIntegrityException.cs b/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetIntegrityException.cs new file mode 100644 index 0000000000..489b1d1539 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetIntegrityException.cs @@ -0,0 +1,67 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Exceptions +{ + using System; + using Microsoft.WinGet.Client.Common; + using Microsoft.WinGet.Client.Properties; + + /// + /// WinGet Integrity exception. + /// + public class WinGetIntegrityException : Exception + { + /// + /// 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; } + + 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, + _ => Resources.IntegrityUnknownMessage, + }; + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetPackageNotInstalledException.cs b/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetPackageNotInstalledException.cs deleted file mode 100644 index 3b5a2432e6..0000000000 --- a/src/PowerShell/Microsoft.WinGet.Client/Exceptions/WinGetPackageNotInstalledException.cs +++ /dev/null @@ -1,26 +0,0 @@ -// ----------------------------------------------------------------------------- -// -// Copyright (c) Microsoft Corporation. Licensed under the MIT License. -// -// ----------------------------------------------------------------------------- - -namespace Microsoft.WinGet.Client.Exceptions -{ - using System; - using Microsoft.WinGet.Client.Properties; - - /// - /// No package found. - /// - [Serializable] - public class WinGetPackageNotInstalledException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public WinGetPackageNotInstalledException() - : base(Resources.WinGetPackageNotInstalledMessage) - { - } - } -} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/AppxModuleHelper.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/AppxModuleHelper.cs new file mode 100644 index 0000000000..43bfe45bb0 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Helpers/AppxModuleHelper.cs @@ -0,0 +1,213 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Helpers +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Management.Automation; + using System.Management.Automation.Runspaces; + using System.Runtime.InteropServices; + using System.Text; + using Microsoft.WinGet.Client.Common; + using Microsoft.WinGet.Client.Properties; + + /// + /// Helper to make calls to the Appx module. + /// + internal class AppxModuleHelper + { + private const string GetAppxModule = "Get-Module Appx"; + private const string ImportModuleCore = "Import-Module Appx -UseWindowsPowerShell"; + private const string GetAppxPackageCommand = "Get-AppxPackage {0}"; + private const string AddAppxPackageFormat = "Add-AppxPackage -Path {0}"; + private const string AddAppxPackageRegisterFormat = "Add-AppxPackage -Path {0} -Register -DisableDevelopmentMode"; + private const string ForceUpdateFromAnyVersion = " -ForceUpdateFromAnyVersion"; + private const string GetAppxPackageByVersionCommand = "Get-AppxPackage {0} | Where-Object -Property Version -eq {1}"; + + private const string AppInstallerName = "Microsoft.DesktopAppInstaller"; + private const string AppxManifest = "AppxManifest.xml"; + private const string PackageFullName = "PackageFullName"; + + // Dependencies + 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 VCLibsUWPDesktopArm = "https://aka.ms/Microsoft.VCLibs.arm.14.00.Desktop.appx"; + private const string VCLibsUWPDesktopArm64 = "https://aka.ms/Microsoft.VCLibs.arm64.14.00.Desktop.appx"; + + private const string UiXaml27 = "Microsoft.UI.Xaml.2.7"; + + private readonly PSCmdlet psCmdlet; + + /// + /// Initializes a new instance of the class. + /// + /// The calling cmdlet. + public AppxModuleHelper(PSCmdlet psCmdlet) + { + this.psCmdlet = psCmdlet; + + // 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. +#if !POWERSHELL_WINDOWS + var appxModule = this.psCmdlet.InvokeCommand.InvokeScript(GetAppxModule); + if (appxModule is null) + { + this.psCmdlet.InvokeCommand.InvokeScript(ImportModuleCore); + } +#endif + } + + /// + /// Calls Get-AppxPackage Microsoft.DesktopAppInstaller. + /// + /// Result of Get-AppxPackage. + public PSObject GetAppInstallerObject() + { + return this.GetAppxObject(AppInstallerName); + } + + /// + /// Gets the string value a property from the Get-AppxPackage object of AppInstaller. + /// + /// Property name. + /// Value, null if doesn't exist. + public string GetAppInstallerPropertyValue(string propertyName) + { + string result = null; + var packageObj = this.GetAppInstallerObject(); + 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; + } + + /// + /// Calls Add-AppxPackage with the specified path. + /// + /// The path of the package to add. + /// If the package version is lower than the installed one. + public void AddAppInstallerBundle(string localPath, bool downgrade = false) + { + // 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.InstallVCLibsDependencies(); + this.InstallUiXaml(); + + StringBuilder sb = new StringBuilder(); + sb.Append(string.Format(AddAppxPackageFormat, localPath)); + + if (downgrade) + { + sb.Append(ForceUpdateFromAnyVersion); + } + + // Using this method simplifies a lot of things, but the error is not propagated with + // the default parameters. PipelineResultTypes.Error will at least output it in the terminal. + this.psCmdlet.InvokeCommand.InvokeScript( + sb.ToString(), + useNewScope: true, + PipelineResultTypes.Error, + input: null); + } + + /// + /// Calls Add-AppxPackage to register with AppInstaller's AppxManifest.xml. + /// + public void RegisterAppInstaller() + { + string packageFullName = this.GetAppInstallerPropertyValue(PackageFullName); + string appxManifestPath = Path.Combine( + Utilities.ProgramFilesWindowsAppPath, + packageFullName, + AppxManifest); + + this.psCmdlet.InvokeCommand.InvokeScript( + string.Format(AddAppxPackageRegisterFormat, appxManifestPath)); + } + + private PSObject GetAppxObject(string packageName) + { + return this.psCmdlet.InvokeCommand + .InvokeScript(string.Format(GetAppxPackageCommand, packageName)) + .FirstOrDefault(); + } + + private IReadOnlyList GetVCLibsDependencies() + { + var vcLibsDependencies = new List(); + var vcLibsPackageObjs = this.psCmdlet.InvokeCommand + .InvokeScript(string.Format(GetAppxPackageByVersionCommand, VCLibsUWPDesktop, VCLibsUWPDesktopVersion)); + if (vcLibsPackageObjs is null || + vcLibsPackageObjs.Count == 0) + { + var arch = RuntimeInformation.OSArchitecture; + if (arch == Architecture.X64) + { + vcLibsDependencies.Add(VCLibsUWPDesktopX64); + } + else if (arch == Architecture.X86) + { + vcLibsDependencies.Add(VCLibsUWPDesktopX86); + } + else if (arch == Architecture.Arm64) + { + // Deployment please figure out for me. + vcLibsDependencies.Add(VCLibsUWPDesktopX64); + vcLibsDependencies.Add(VCLibsUWPDesktopX86); + vcLibsDependencies.Add(VCLibsUWPDesktopArm); + vcLibsDependencies.Add(VCLibsUWPDesktopArm64); + } + else + { + throw new PSNotSupportedException(arch.ToString()); + } + } + else + { + this.psCmdlet.WriteDebug($"VCLibs are updated."); + } + + return vcLibsDependencies; + } + + private void InstallVCLibsDependencies() + { + var packages = this.GetVCLibsDependencies(); + foreach (var package in packages) + { + this.psCmdlet.WriteDebug($"Installing VCLibs {package}"); + this.psCmdlet.InvokeCommand.InvokeScript(string.Format(AddAppxPackageFormat, package)); + } + } + + private void InstallUiXaml() + { + // TODO: We need to follow up for Microsoft.UI.Xaml.2.7 + // downloading the nuget and extracting it doesn't sound like the right thing to do. + var uiXamlObjs = this.GetAppxObject(UiXaml27); + if (uiXamlObjs is null) + { + throw new PSNotImplementedException(Resources.MicrosoftUIXaml27Message); + } + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs index 98a28fbfed..ccd28ccb41 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs +++ b/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Client.Factories using System; using System.Runtime.InteropServices; using Microsoft.Management.Deployment; - using Microsoft.WinGet.Client.Common; + using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Exceptions; #if NET @@ -123,7 +123,7 @@ private static T Create(Type type, in Guid iid) { if (hr == ErrorCode.FileNotFound) { - throw new WinGetPackageNotInstalledException(); + throw new WinGetIntegrityException(IntegrityCategory.AppInstallerNotInstalled); } else { diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/GitHubRelease.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/GitHubRelease.cs new file mode 100644 index 0000000000..3e96d1c1c0 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Helpers/GitHubRelease.cs @@ -0,0 +1,127 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Helpers +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Threading.Tasks; + using Octokit; + using FileMode = System.IO.FileMode; + + /// + /// Handles WinGet's releases in GitHub. + /// + internal class GitHubRelease + { + private const string Owner = "microsoft"; + private const string Repo = "winget-cli"; + private const string UserAgent = "winget-powershell"; + private const string MsixBundleName = "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"; + private const string ContentType = "application/octet-stream"; + + private readonly IGitHubClient gitHubClient; + + /// + /// Initializes a new instance of the class. + /// + public GitHubRelease() + { + this.gitHubClient = new GitHubClient(new ProductHeaderValue(UserAgent)); + } + + /// + /// Download a release from winget-cli. + /// + /// Optional release name. If null, gets latest. + /// Path where the msix bundle is downloaded. + public string DownloadRelease(string releaseTag) + { + return this.DownloadReleaseAsync(releaseTag).GetAwaiter().GetResult(); + } + + /// + /// Gets the latest released version and waits. + /// + /// Include prerelease. + /// Latest version. + public string GetLatestVersionTagName(bool includePreRelease) + { + return this.GetLatestVersionAsync(includePreRelease).GetAwaiter().GetResult().TagName; + } + + /// + /// Downloads a file from a url and waits. + /// + /// Url. + /// File name. + public void DownloadUrl(string url, string fileName) + { + this.DownloadUrlAsync(url, fileName).GetAwaiter().GetResult(); + } + + /// + /// Download asynchronously a release from winget-cli. + /// + /// Optional release name. If null, gets latest. + /// Path where the msix bundle is downloaded. + public async Task DownloadReleaseAsync(string releaseTag) + { + Release release = await this.gitHubClient.Repository.Release.Get(Owner, Repo, releaseTag); + + // Get asset and download. + var msixBundleAsset = release.Assets.Where(a => a.Name == MsixBundleName).First(); + + var tmpFile = Path.GetTempFileName(); + await this.DownloadUrlAsync(msixBundleAsset.Url, tmpFile); + return tmpFile; + } + + /// + /// Downloads a file from a url. + /// + /// Url. + /// File name. + /// A representing the asynchronous operation. + public async Task DownloadUrlAsync(string url, string fileName) + { + var response = await this.gitHubClient.Connection.Get( + new Uri(url), + new Dictionary(), + ContentType); + + using var memoryStream = new MemoryStream((byte[])response.Body); + using var fileStream = File.Open(fileName, FileMode.Open); + memoryStream.Position = 0; + await memoryStream.CopyToAsync(fileStream); + } + + /// + /// Gets the latest released version. + /// + /// Include prerelease. + /// Latest version. + internal async Task GetLatestVersionAsync(bool includePreRelease) + { + Release release; + + // GetLatest doesn't respect prerelease or gives an option to get it. + if (includePreRelease) + { + // GetAll orders by newest and includes pre releases. + release = (await this.gitHubClient.Repository.Release.GetAll(Owner, Repo))[0]; + } + else + { + release = await this.gitHubClient.Repository.Release.GetLatest(Owner, Repo); + } + + return release; + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/WinGetVersion.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/WinGetVersion.cs new file mode 100644 index 0000000000..65dd303121 --- /dev/null +++ b/src/PowerShell/Microsoft.WinGet.Client/Helpers/WinGetVersion.cs @@ -0,0 +1,118 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Client.Helpers +{ + using System; + + /// + /// WinGetVersion. Parse the string version returned by winget --version to allow comparisons. + /// + internal class WinGetVersion + { + /// + /// Initializes a new instance of the class. + /// + /// String Version. + public WinGetVersion(string version) + { + if (string.IsNullOrEmpty(version)) + { + throw new ArgumentNullException(); + } + + string toParseVersion = version; + + // WinGet version starts with v + if (toParseVersion[0] == 'v') + { + this.TagVersion = version; + toParseVersion = toParseVersion.Substring(1); + } + else + { + // WinGet version always start with v. + this.TagVersion = 'v' + version; + } + + // WinGet version might end with -preview + if (toParseVersion.EndsWith("-preview")) + { + this.IsPrerelease = true; + toParseVersion = toParseVersion.Substring(0, toParseVersion.IndexOf('-')); + } + + this.Version = Version.Parse(toParseVersion); + } + + /// + /// Gets the version of the installed winget. + /// + public static WinGetVersion InstalledWinGetVersion + { + get + { + var wingetCliWrapper = new WingetCLIWrapper(); + var result = wingetCliWrapper.RunCommand("--version"); + return new WinGetVersion(result.StdOut.Replace(Environment.NewLine, string.Empty)); + } + } + + /// + /// Gets the version as it appears as a tag. + /// + public string TagVersion { get; } + + /// + /// Gets the version. + /// + public System.Version Version { get; } + + /// + /// Gets a value indicating whether is this version is a prerelease. + /// + public bool IsPrerelease { get; } + + /// + /// Version.CompareTo taking into account prerelease. + /// From semver: Pre-release versions have a lower precedence than the associated normal version. + /// + /// Other winget version. + /// + /// A signed integer that indicates the relative values of the two objects. Less than 0 + /// means this version is before other version. 0 means they are equal. Greater than 0 + /// means this version is greater. + /// + public int CompareTo(WinGetVersion otherVersion) + { + if (this.IsPrerelease && !otherVersion.IsPrerelease) + { + return -1; + } + + if (!this.IsPrerelease && otherVersion.IsPrerelease) + { + return 1; + } + + return this.Version.CompareTo(otherVersion.Version); + } + + /// + /// Deployment doesn't care about semver or prerelease builds. + /// + /// Other version. + /// + /// A signed integer that indicates the relative values of the two objects. Less than 0 + /// means this version is before other version. 0 means they are equal. Greater than 0 + /// means this version is greater. + /// + public int CompareAsDeployment(WinGetVersion otherVersion) + { + return this.Version.CompareTo(otherVersion.Version); + } + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client/Helpers/WingetCLIWrapper.cs b/src/PowerShell/Microsoft.WinGet.Client/Helpers/WingetCLIWrapper.cs index 8e212d0893..c7695928d7 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Helpers/WingetCLIWrapper.cs +++ b/src/PowerShell/Microsoft.WinGet.Client/Helpers/WingetCLIWrapper.cs @@ -6,9 +6,9 @@ namespace Microsoft.WinGet.Client.Helpers { - using System; using System.Diagnostics; using System.IO; + using Microsoft.WinGet.Client.Common; using Microsoft.WinGet.Client.Exceptions; /// @@ -16,40 +16,43 @@ namespace Microsoft.WinGet.Client.Helpers /// internal class WingetCLIWrapper { - private static readonly string WingetCliPath; + /// + /// The file name to use in start info. + /// + private readonly string wingetPath; /// - /// Initializes static members of the class. + /// 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. /// - static WingetCLIWrapper() + /// Use full path or not. + public WingetCLIWrapper(bool fullPath = true) { - string windowsAppPath = Environment.ExpandEnvironmentVariables("%LOCALAPPDATA%\\Microsoft\\WindowsApps"); -#if USE_PROD_CLSIDS - WingetCliPath = Path.Combine( - windowsAppPath, - "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe", - "winget.exe"); -#else - WingetCliPath = Path.Combine( - windowsAppPath, - "WinGetDevCLI_8wekyb3d8bbwe", - "wingetdev.exe"); -#endif + if (fullPath) + { + this.wingetPath = WinGetFullPath; + } + else + { + this.wingetPath = Constants.WinGetExe; + } } /// - /// Initializes a new instance of the class. + /// Gets the full path of winget executable. /// - public WingetCLIWrapper() + public static string WinGetFullPath { - if (!File.Exists(WingetCliPath)) + get { - throw new WinGetPackageNotInstalledException(); + return Path.Combine( + Utilities.LocalDataWindowsAppPath, + Constants.WingetPackageFamilyName, + Constants.WinGetExe); } } @@ -60,11 +63,17 @@ public WingetCLIWrapper() /// Parameters. /// Time out. /// WinGetCommandResult. - public WinGetCLICommandResult RunCommand(string command, string parameters, int timeOut = 60000) + public WinGetCLICommandResult RunCommand(string command, string parameters = null, int timeOut = 60000) { + string args = command; + if (!string.IsNullOrEmpty(parameters)) + { + args += ' ' + parameters; + } + Process p = new () { - StartInfo = new (WingetCliPath, command + ' ' + parameters) + StartInfo = new (this.wingetPath, args) { UseShellExecute = false, RedirectStandardOutput = true, @@ -84,7 +93,7 @@ public WinGetCLICommandResult RunCommand(string command, string parameters, int p.StandardError.ReadToEnd()); } - throw new TimeoutException($"Direct winget command run timed out: {command} {parameters}"); + throw new WinGetCLITimeoutException(command, parameters); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj b/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj index df46a223e8..cb9aa65253 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj +++ b/src/PowerShell/Microsoft.WinGet.Client/Microsoft.WinGet.Client.csproj @@ -26,6 +26,7 @@ + all diff --git a/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psd1 b/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psd1 index badf730a06..2b5eda60b4 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psd1 +++ b/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psd1 @@ -75,7 +75,6 @@ else { # 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 = @( - 'Get-WinGetVersion' 'Enable-WinGetSetting', 'Disable-WinGetSetting', 'Add-WinGetSource', @@ -86,6 +85,7 @@ 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', @@ -94,7 +94,9 @@ CmdletsToExport = @( 'Update-WinGetPackage', 'Get-WinGetUserSettings', 'Set-WinGetUserSettings', - 'Test-WinGetUserSettings' + 'Test-WinGetUserSettings', + 'Assert-WinGetPackageManager', + 'Repair-WinGetPackageManager' ) # Variables to export from this module diff --git a/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psm1 b/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psm1 index 497d9c09e1..859cac34df 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psm1 +++ b/src/PowerShell/Microsoft.WinGet.Client/Module/Microsoft.WinGet.Client.psm1 @@ -1,123 +1,37 @@ # Module created by Microsoft.PowerShell.Crescendo class PowerShellCustomFunctionAttribute : System.Attribute { - [bool]$RequiresElevation - [string]$Source - PowerShellCustomFunctionAttribute() { $this.RequiresElevation = $false; $this.Source = "Microsoft.PowerShell.Crescendo" } - PowerShellCustomFunctionAttribute([bool]$rElevation) { - $this.RequiresElevation = $rElevation - $this.Source = "Microsoft.PowerShell.Crescendo" - } + [bool]$RequiresElevation + [string]$Source + PowerShellCustomFunctionAttribute() { $this.RequiresElevation = $false; $this.Source = "Microsoft.PowerShell.Crescendo" } + PowerShellCustomFunctionAttribute([bool]$rElevation) { + $this.RequiresElevation = $rElevation + $this.Source = "Microsoft.PowerShell.Crescendo" + } } <# - .SYNOPSIS - Displays the version of the tool. +.SYNOPSIS +Enables the WinGet setting specified by the `Name` parameter. - .DESCRIPTION - Displays the version of the winget.exe tool. +.DESCRIPTION +Enables the WinGet setting specified by the `Name` parameter. +Supported settings: + - LocalManifestFiles + - BypassCertificatePinningForMicrosoftStore + - InstallerHashOverride + - LocalArchiveMalwareScanOverride - .INPUTS - None. +.PARAMETER Name +Specifies the name of the setting to be enabled. - .OUTPUTS - None +.INPUTS +None. - .EXAMPLE - PS> Get-WinGetVersion -#> -function Get-WinGetVersion -{ -[PowerShellCustomFunctionAttribute(RequiresElevation=$False)] -[CmdletBinding(SupportsShouldProcess)] - -param( ) +.OUTPUTS +None -BEGIN { - $__PARAMETERMAP = @{} - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } -} - -PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += '--version' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS -} - -<# - .SYNOPSIS - Enables the WinGet setting specified by the `Name` parameter. - - .DESCRIPTION - Enables the WinGet setting specified by the `Name` parameter. - Supported settings: - - LocalManifestFiles - - BypassCertificatePinningForMicrosoftStore - - InstallerHashOverride - - LocalArchiveMalwareScanOverride - - .PARAMETER Name - Specifies the name of the setting to be enabled. - - .INPUTS - None. - - .OUTPUTS - None - - .EXAMPLE - PS> Enable-WinGetSetting -name LocalManifestFiles +.EXAMPLE +PS> Enable-WinGetSetting -name LocalManifestFiles #> function Enable-WinGetSetting { @@ -127,105 +41,105 @@ function Enable-WinGetSetting param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Name - ) + ) BEGIN { - $__PARAMETERMAP = @{ - Name = @{ - OriginalName = '' - OriginalPosition = '0' - Position = '0' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - } - - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'settings' - $__commandArgs += '--enable' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'settings' + $__commandArgs += '--enable' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } <# - .SYNOPSIS - Disables the WinGet setting specified by the `Name` parameter. +.SYNOPSIS +Disables the WinGet setting specified by the `Name` parameter. - .DESCRIPTION - Disables the WinGet setting specified by the `Name` parameter. - Supported settings: - - LocalManifestFiles - - BypassCertificatePinningForMicrosoftStore - - InstallerHashOverride - - LocalArchiveMalwareScanOverride +.DESCRIPTION +Disables the WinGet setting specified by the `Name` parameter. +Supported settings: + - LocalManifestFiles + - BypassCertificatePinningForMicrosoftStore + - InstallerHashOverride + - LocalArchiveMalwareScanOverride - .PARAMETER Name - Specifies the name of the setting to be disabled. +.PARAMETER Name +Specifies the name of the setting to be disabled. - .INPUTS - None. +.INPUTS +None. - .OUTPUTS - None +.OUTPUTS +None - .EXAMPLE - PS> Disable-WinGetSetting -name LocalManifestFiles +.EXAMPLE +PS> Disable-WinGetSetting -name LocalManifestFiles #> function Disable-WinGetSetting { @@ -235,100 +149,100 @@ function Disable-WinGetSetting param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Name - ) + ) BEGIN { - $__PARAMETERMAP = @{ - Name = @{ - OriginalName = '' - OriginalPosition = '0' - Position = '0' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - } - - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'settings' - $__commandArgs += '--disable' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'settings' + $__commandArgs += '--disable' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } <# - .SYNOPSIS - Get winget settings. +.SYNOPSIS +Get winget settings. - .DESCRIPTION - Get the administrator settings values as well as the location of the user settings as json string +.DESCRIPTION +Get the administrator settings values as well as the location of the user settings as json string - .PARAMETER Name - None +.PARAMETER Name +None - .INPUTS - None. +.INPUTS +None. - .OUTPUTS - Prints the export settings json. +.OUTPUTS +Prints the export settings json. - .EXAMPLE - PS> Get-WinGetSettings +.EXAMPLE +PS> Get-WinGetSettings #> function Get-WinGetSettings { @@ -338,94 +252,94 @@ function Get-WinGetSettings param( ) BEGIN { - $__PARAMETERMAP = @{} - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{} + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'settings' - $__commandArgs += 'export' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'settings' + $__commandArgs += 'export' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } <# - .SYNOPSIS - Add a new source. +.SYNOPSIS +Add a new source. - .DESCRIPTION - 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. +.DESCRIPTION +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. - .PARAMETER Name - Name of the source. +.PARAMETER Name +Name of the source. - .PARAMETER Argument - Argument to be given to the source. +.PARAMETER Argument +Argument to be given to the source. - .PARAMETER Type - Type of the source. +.PARAMETER Type +Type of the source. - .INPUTS - None. +.INPUTS +None. - .OUTPUTS - None. +.OUTPUTS +None. - .EXAMPLE - PS> Add-WinGetSource -Name Contoso -Argument https://www.contoso.com/cache +.EXAMPLE +PS> Add-WinGetSource -Name Contoso -Argument https://www.contoso.com/cache #> function Add-WinGetSource @@ -440,116 +354,116 @@ param( [string]$Argument, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [string]$Type - ) + ) BEGIN { - $__PARAMETERMAP = @{ - Name = @{ - OriginalName = '--name' - OriginalPosition = '0' - Position = '0' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - Argument = @{ - OriginalName = '--arg' - OriginalPosition = '0' - Position = '1' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - Type = @{ - OriginalName = '--type' - OriginalPosition = '0' - Position = '2' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - } - - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '--name' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + Argument = @{ + OriginalName = '--arg' + OriginalPosition = '0' + Position = '1' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + Type = @{ + OriginalName = '--type' + OriginalPosition = '0' + Position = '2' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'source' - $__commandArgs += 'add' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'source' + $__commandArgs += 'add' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } <# - .SYNOPSIS - Remove a specific source. +.SYNOPSIS +Remove a specific source. - .DESCRIPTION - Remove a specific source. The source must already exist to be removed. +.DESCRIPTION +Remove a specific source. The source must already exist to be removed. - .PARAMETER Name - Name of the source. +.PARAMETER Name +Name of the source. - .INPUTS - None. +.INPUTS +None. - .OUTPUTS - None. +.OUTPUTS +None. - .EXAMPLE - PS> Remove-WinGetSource -Name Contoso +.EXAMPLE +PS> Remove-WinGetSource -Name Contoso #> function Remove-WinGetSource @@ -560,104 +474,104 @@ function Remove-WinGetSource param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)] [string]$Name - ) + ) BEGIN { - $__PARAMETERMAP = @{ - Name = @{ - OriginalName = '--name' - OriginalPosition = '0' - Position = '0' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - } - - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '--name' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'source' - $__commandArgs += 'remove' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'source' + $__commandArgs += 'remove' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } <# - .SYNOPSIS - Drops existing sources. Without any argument, this command will drop all sources and add the defaults. +.SYNOPSIS +Drops existing sources. Without any argument, this command will drop all sources and add the defaults. - .DESCRIPTION - 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. +.DESCRIPTION +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. - .PARAMETER Name - Name of the source. +.PARAMETER Name +Name of the source. - .INPUTS - None. +.INPUTS +None. - .OUTPUTS - None. +.OUTPUTS +None. - .EXAMPLE - PS> Reset-WinGetSource +.EXAMPLE +PS> Reset-WinGetSource - .EXAMPLE - PS> Reset-WinGetSource -Name Contoso +.EXAMPLE +PS> Reset-WinGetSource -Name Contoso #> function Reset-WinGetSource @@ -668,81 +582,80 @@ function Reset-WinGetSource param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Name - ) + ) BEGIN { - $__PARAMETERMAP = @{ - Name = @{ - OriginalName = '--name' - OriginalPosition = '0' - Position = '0' - ParameterType = 'string' - ApplyToExecutable = $False - NoGap = $False - } - } - - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '--name' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } } PROCESS { - $__boundParameters = $PSBoundParameters - $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name - $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) - $__commandArgs = @() - $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) - if ($__boundParameters["Debug"]){wait-debugger} - $__commandArgs += 'source' - $__commandArgs += 'reset' - $__commandArgs += '--force' - foreach ($paramName in $__boundParameters.Keys| - Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| - Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { - $value = $__boundParameters[$paramName] - $param = $__PARAMETERMAP[$paramName] - if ($param) { - if ($value -is [switch]) { - if ($value.IsPresent) { - if ($param.OriginalName) { $__commandArgs += $param.OriginalName } - } - elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } - } - elseif ( $param.NoGap ) { - $pFmt = "{0}{1}" - if($value -match "\s") { $pFmt = "{0}""{1}""" } - $__commandArgs += $pFmt -f $param.OriginalName, $value - } - else { - if($param.OriginalName) { $__commandArgs += $param.OriginalName } - $__commandArgs += $value | Foreach-Object {$_} - } - } - } - $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} - if ($__boundParameters["Debug"]){wait-debugger} - if ( $__boundParameters["Verbose"]) { - Write-Verbose -Verbose -Message winget.exe - $__commandArgs | Write-Verbose -Verbose - } - $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] - if (! $__handlerInfo ) { - $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present - } - $__handler = $__handlerInfo.Handler - if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { - # check for the application and throw if it cannot be found - if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { - throw "Cannot find executable 'winget.exe'" - } - if ( $__handlerInfo.StreamOutput ) { - & "winget.exe" $__commandArgs | & $__handler - } - else { - $result = & "winget.exe" $__commandArgs - & $__handler $result - } - } - } # end PROCESS + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'source' + $__commandArgs += 'reset' + $__commandArgs += '--force' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message winget.exe + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("winget.exe $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "winget.exe")) { + throw "Cannot find executable 'winget.exe'" + } + if ( $__handlerInfo.StreamOutput ) { + & "winget.exe" $__commandArgs | & $__handler + } + else { + $result = & "winget.exe" $__commandArgs + & $__handler $result + } + } +} # end PROCESS } - diff --git a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs index 8068aba430..5ada78fc8f 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs +++ b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to 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.. + /// + internal static string AppExecutionAliasDisabledHelpMessage { + get { + return ResourceManager.GetString("AppExecutionAliasDisabledHelpMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to An error occurred while connecting to the catalog.. /// @@ -87,6 +96,87 @@ internal static string FindPackagesExceptionMessage { } } + /// + /// Looks up a localized string similar to The App Execution Alias for the Windows Package Manager is disabled.. + /// + internal static string IntegrityAppExecutionAliasDisabledMessage { + get { + return ResourceManager.GetString("IntegrityAppExecutionAliasDisabledMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The App Installer is not installed.. + /// + internal static string IntegrityAppInstallerNotInstalledMessage { + get { + return ResourceManager.GetString("IntegrityAppInstallerNotInstalledMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The App Installer is not registered.. + /// + internal static string IntegrityAppInstallerNotRegisteredMessage { + get { + return ResourceManager.GetString("IntegrityAppInstallerNotRegisteredMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The App Installer does not contain the Windows Package Manager.. + /// + internal static string IntegrityAppInstallerNotSupportedMessage { + get { + return ResourceManager.GetString("IntegrityAppInstallerNotSupportedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Windows Package Manager returned an unexcepted result.. + /// + internal static string IntegrityFailureMessage { + get { + return ResourceManager.GetString("IntegrityFailureMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The App Installer did not automatically add the PATH environment variable.. + /// + internal static string IntegrityNotInPathMessage { + get { + return ResourceManager.GetString("IntegrityNotInPathMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The Windows Package Manager requires at least Version 1809 (October 2018 Update).. + /// + internal static string IntegrityOsNotSupportedMessage { + get { + return ResourceManager.GetString("IntegrityOsNotSupportedMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The installed winget version doesn't match the expectation. Installer version '{0}' Expected version '{1}'. + /// + internal static string IntegrityUnexpectedVersionMessage { + get { + return ResourceManager.GetString("IntegrityUnexpectedVersionMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to execute winget command.. + /// + internal static string IntegrityUnknownMessage { + get { + return ResourceManager.GetString("IntegrityUnknownMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to No source matches the given value: {0}. /// @@ -105,6 +195,15 @@ internal static string InvalidVersionExceptionMessage { } } + /// + /// Looks up a localized string similar to Microsoft.UI.Xaml.2.7 package is not installed. + /// + internal static string MicrosoftUIXaml27Message { + get { + return ResourceManager.GetString("MicrosoftUIXaml27Message", resourceCulture); + } + } + /// /// Looks up a localized string similar to No packages matched the given input criteria.. /// @@ -169,11 +268,20 @@ internal static string WinGetCLIExceptionMessage { } /// - /// Looks up a localized string similar to Unable to execute command; WinGet package not installed.. + /// Looks up a localized string similar to Winget command run timed out: {0} {1}. + /// + internal static string WinGetCLITimeoutExceptionMessage { + get { + return ResourceManager.GetString("WinGetCLITimeoutExceptionMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Windows Package Manager not supported.. /// - internal static string WinGetPackageNotInstalledMessage { + internal static string WinGetNotSupportedMessage { get { - return ResourceManager.GetString("WinGetPackageNotInstalledMessage", resourceCulture); + return ResourceManager.GetString("WinGetNotSupportedMessage", resourceCulture); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx index 89abdcce96..abe71c3060 100644 --- a/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx +++ b/src/PowerShell/Microsoft.WinGet.Client/Properties/Resources.resx @@ -119,48 +119,88 @@ No source matches the given value: {0} - {0} - The name of the source that was not found. + {Locked="{0}"} {0} - The name of the source that was not found. This cmdlet is currently disabled for SYSTEM. An error occurred while searching for packages: {0} - {0} - A string representation of the error status returned by the catalog. + {Locked="{0}"} {0} - A string representation of the error status returned by the catalog. Installing '{0}' - {0} - The name of the package being installed. + {Locked="{0}"} {0} - The name of the package being installed. Uninstalling '{0}' - {0} - The name of the package being uninstalled. + {Locked="{0}"} {0} - The name of the package being uninstalled. Updating '{0}' - {0} - The name of the package being updated. + {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} - {0} - The version string provided by the user. + {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. - {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. + {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 command; WinGet package not installed. + + Unable to execute winget command. Command {0} failed with exit code {1} + {Locked="{0}","{1}"} {0} - The winget command executed. {1} - 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 run 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. + + + Microsoft.UI.Xaml.2.7 package is not installed + {Locked="Microsoft.UI.Xaml.2.7"} + + + Windows Package Manager not supported. + + + 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. + \ No newline at end of file diff --git a/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1 b/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1 index 4fde00d764..c8d6ba1218 100644 --- a/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1 +++ b/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psd1 @@ -82,9 +82,10 @@ # DSC resources to export from this module DscResourcesToExport = @( - 'WinGetUserSettingsResource' + 'WinGetUserSettings' 'WinGetAdminSettings' - 'WinGetSourcesResource' + 'WinGetSources' + 'WinGetPackageManager' ) # List of all modules packaged with this module diff --git a/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 b/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 index 0e1a23842c..74e5ed6fb6 100644 --- a/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 +++ b/src/PowerShell/Microsoft.WinGet.DSC/Microsoft.WinGet.DSC.psm1 @@ -43,7 +43,7 @@ enum Ensure # This resource is in charge of managing the settings.json file of winget. [DSCResource()] -class WinGetUserSettingsResource +class WinGetUserSettings { # We need a key. Do not set. [DscProperty(Key)] @@ -57,7 +57,7 @@ class WinGetUserSettingsResource [WinGetAction]$Action = [WinGetAction]::Full # Gets the current UserSettings by looking at the settings.json file for the current user. - [WinGetUserSettingsResource] Get() + [WinGetUserSettings] Get() { Assert-WinGetCommand "Get-WinGetUserSettings" @@ -172,7 +172,7 @@ class WinGetAdminSettings } [DSCResource()] -class WinGetSourcesResource +class WinGetSources { # We need a key. Do not set. [DscProperty(Key)] @@ -192,7 +192,7 @@ class WinGetSourcesResource [WinGetAction]$Action = [WinGetAction]::Full # Gets the current sources on winget. - [WinGetSourcesResource] Get() + [WinGetSources] Get() { Assert-WinGetCommand "Get-WinGetSource" $packageCatalogReferences = Get-WinGetSource @@ -304,4 +304,98 @@ class WinGetSourcesResource } } +# TODO: It would be nice if these resource has a non configurable property that has extra information that comes from +# GitHub. We could implement it here or add more cmdlets in Microsoft.WinGet.Client. +[DSCResource()] +class WinGetPackageManager +{ + # We need a key. Do not set. + [DscProperty(Key)] + [string]$SID + + [DscProperty()] + [string]$Version = "" + + [DscProperty()] + [bool]$UseLatest + + [DscProperty()] + [bool]$UseLatestPreRelease + + # If winget is not installed the version will be empty. + [WinGetPackageManager] Get() + { + $integrityResource = [WinGetPackageManager]::new() + if ($integrityResource.Test()) + { + $integrityResource.Version = Get-WinGetVersion + } + + return $integrityResource + } + + # Tests winget is installed. + [bool] Test() + { + Assert-WinGetCommand "Assert-WinGetPackageManager" + Assert-WinGetCommand "Get-WinGetVersion" + + try + { + $hashArgs = @{} + + if ($this.UseLatest) + { + $hashArgs.Add("Latest", $true) + } elseif ($this.UseLatestPreRelease) + { + $hashArgs.Add("Latest", $true) + $hashArgs.Add("IncludePreRelease", $true) + } elseif (-not [string]::IsNullOrWhiteSpace($this.Version)) + { + $hashArgs.Add("Version", $this.Version) + } + + Assert-WinGetPackageManager @hashArgs + } + catch + { + return $false + } + + return $true + } + + # Repairs Winget. + [void] Set() + { + Assert-WinGetCommand "Repair-WinGetPackageManager" + + if (-not $this.Test()) + { + $result = -1 + $hashArgs = @{} + + if ($this.UseLatest) + { + $hashArgs.Add("Latest", $true) + } elseif ($this.UseLatestPreRelease) + { + $hashArgs.Add("Latest", $true) + $hashArgs.Add("IncludePreRelease", $true) + } elseif (-not [string]::IsNullOrWhiteSpace($this.Version)) + { + $hashArgs.Add("Version", $this.Version) + } + + $result = Repair-WinGetPackageManager @hashArgs + + if ($result -ne 0) + { + throw "Failed to repair winget. Result $result" + } + } + } +} + #endregion DscResources diff --git a/src/PowerShell/scripts/Initialize-LocalWinGetModules.ps1 b/src/PowerShell/scripts/Initialize-LocalWinGetModules.ps1 index d5500aaec9..956b55f521 100644 --- a/src/PowerShell/scripts/Initialize-LocalWinGetModules.ps1 +++ b/src/PowerShell/scripts/Initialize-LocalWinGetModules.ps1 @@ -25,7 +25,10 @@ param ( [Parameter(Mandatory)] [string] - $Configuration + $Configuration, + + [switch] + $SkipImportModule ) class WinGetModule @@ -96,8 +99,11 @@ if (-not $env:PSModulePath.Contains($moduleRootOutput)) } # Now import modules. -foreach($module in $modules) +if (-not $SkipImportModule) { - Write-Host "Importing module $($module.Name)" -ForegroundColor Green - Import-Module $module.Name -Force + foreach($module in $modules) + { + Write-Host "Importing module $($module.Name)" -ForegroundColor Green + Import-Module $module.Name -Force + } } diff --git a/src/PowerShell/scripts/samples/WinGetAdminSettingsResourceSample.ps1 b/src/PowerShell/scripts/samples/WinGetAdminSettingsResourceSample.ps1 index 21f6556ae9..931687d083 100644 --- a/src/PowerShell/scripts/samples/WinGetAdminSettingsResourceSample.ps1 +++ b/src/PowerShell/scripts/samples/WinGetAdminSettingsResourceSample.ps1 @@ -4,9 +4,8 @@ <# .SYNOPSIS Simple sample on how to use WinGetAdminSettings DSC resource. - Requires PSDesiredStateConfiguration v2 and enabling the - PSDesiredStateConfiguration.InvokeDscResource experimental feature - `Enable-ExperimentalFeature -Name PSDesiredStateConfiguration.InvokeDscResource` + Requires PSDesiredStateConfiguration version 2.0.6 + IMPORTANT: This will leave LocalManifestFiles enabled Run as admin for set. #> diff --git a/src/PowerShell/scripts/samples/WinGetPackageManagerSample.ps1 b/src/PowerShell/scripts/samples/WinGetPackageManagerSample.ps1 new file mode 100644 index 0000000000..db679e989b --- /dev/null +++ b/src/PowerShell/scripts/samples/WinGetPackageManagerSample.ps1 @@ -0,0 +1,127 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# + .SYNOPSIS + Simple sample on how to use WinGetIntegrity DSC resource. + Requires PSDesiredStateConfiguration version 2.0.6 + + IMPORTANT: This will modify the winget you have installed +#> + +#Requires -Modules Microsoft.WinGet.Client, Microsoft.WinGet.DSC + +using module Microsoft.WinGet.DSC + +$resource = @{ + Name = 'WinGetPackageManager' + ModuleName = 'Microsoft.WinGet.DSC' + Property = @{ + } +} + +# This just demonstrate the Get command. +$getResult = Invoke-DscResource @resource -Method Get + +if (-not([string]::IsNullOrWhiteSpace($getResult.Version))) +{ + Write-Host "Current winget version $($getResult.Version)" +} +else +{ + # If the result of get contains an empty version, it means that winget is not installed. This is not the right + # way to do it though, is better to call Test with an empty Version property. + if (-not (Invoke-DscResource @resource -Method Test).InDesiredState) + { + Write-Host "winget is not installed" + } + else + { + Write-Error "BUG BUG!!" + return + } +} + +# At the time I'm doing this the second latest released winget version is v1.3.2091. Lets assume you want to stay there forever. +$v132091 = "v1.3.2091" +$resource = @{ + Name = 'WinGetPackageManager' + ModuleName = 'Microsoft.WinGet.DSC' + Property = @{ + Version = $v132091 + } +} + +$testResult = Invoke-DscResource @resource -Method Test +if ($testResult.InDesiredState) +{ + Write-Host "winget is already in a good state (aka in version $v132091)" +} +else +{ + # Oh no, we are not in a good state. Lets get you there. + # Internally, Set calls Repair-WinGet -Version v1.3.2691 which means that it will try to repair winget + # by downloading v1.3.2691 and installing it if needed. For example, if your AppInstaller is not registered + # it will register the package and then verify the specified version is installed. + Invoke-DscResource @resource -Method Set | Out-Null + + # Now this should work. + $testResult = Invoke-DscResource @resource -Method Test + if ($testResult.InDesiredState) + { + Write-Host "winget is in a good state (aka in version $v132091)" + } + else + { + Write-Error "BUG BUG!!" + return + } +} + +# Now, lets say that you want to have always the latest winget installed. You can specify UseLatest. +$resource = @{ + Name = 'WinGetPackageManager' + ModuleName = 'Microsoft.WinGet.DSC' + Property = @{ + UseLatest = $true + } +} +$testResult = Invoke-DscResource @resource -Method Test +if ($testResult.InDesiredState) +{ + Write-Host "winget version is the latest version" +} +else +{ + Write-Host "winget version is not latest version" +} + +# You can also do UseLatestPreRelease +$resource = @{ + Name = 'WinGetPackageManager' + ModuleName = 'Microsoft.WinGet.DSC' + Property = @{ + UseLatestPreRelease = $true + } +} +$testResult = Invoke-DscResource @resource -Method Test +if ($testResult.InDesiredState) +{ + Write-Host "winget version is the latest prerelease version" +} +else +{ + # Get the latest prerelease. + Invoke-DscResource @resource -Method Set | Out-Null + + $testResult = Invoke-DscResource @resource -Method Test + if ($testResult.InDesiredState) + { + Write-Host "winget version is the latest prerelease version" + } + else + { + Write-Error "BUG BUG!!" + return + } +} diff --git a/src/PowerShell/scripts/samples/WinGetSourcesResourceSample.ps1 b/src/PowerShell/scripts/samples/WinGetSourcesSample.ps1 similarity index 88% rename from src/PowerShell/scripts/samples/WinGetSourcesResourceSample.ps1 rename to src/PowerShell/scripts/samples/WinGetSourcesSample.ps1 index 16460a7fd4..3148671266 100644 --- a/src/PowerShell/scripts/samples/WinGetSourcesResourceSample.ps1 +++ b/src/PowerShell/scripts/samples/WinGetSourcesSample.ps1 @@ -4,9 +4,8 @@ <# .SYNOPSIS Simple sample on how to use WinGetSourcesResource DSC resource. - Requires PSDesiredStateConfiguration v2 and enabling the - PSDesiredStateConfiguration.InvokeDscResource experimental feature - `Enable-ExperimentalFeature -Name PSDesiredStateConfiguration.InvokeDscResource` + Requires PSDesiredStateConfiguration version 2.0.6 + IMPORTANT: This deletes the main winget source and add it again. Run as admin for set. #> diff --git a/src/PowerShell/scripts/samples/WinGetUserSettingsResourceSample.ps1 b/src/PowerShell/scripts/samples/WinGetUserSettingsPackageManagerSample.ps1 similarity index 83% rename from src/PowerShell/scripts/samples/WinGetUserSettingsResourceSample.ps1 rename to src/PowerShell/scripts/samples/WinGetUserSettingsPackageManagerSample.ps1 index daebd41549..1a169e2ae5 100644 --- a/src/PowerShell/scripts/samples/WinGetUserSettingsResourceSample.ps1 +++ b/src/PowerShell/scripts/samples/WinGetUserSettingsPackageManagerSample.ps1 @@ -4,9 +4,7 @@ <# .SYNOPSIS Simple sample on how to use WinGetUserSettings DSC resource. - Requires PSDesiredStateConfiguration v2 and enabling the - PSDesiredStateConfiguration.InvokeDscResource experimental feature - `Enable-ExperimentalFeature -Name PSDesiredStateConfiguration.InvokeDscResource` + Requires PSDesiredStateConfiguration version 2.0.6 IMPORTANT: If you loaded the released modules this will modify your settings. Use the -Restore to get back to your original settings @@ -27,7 +25,7 @@ param ( ) $resource = @{ - Name = 'WinGetUserSettingsResource' + Name = 'WinGetUserSettings' ModuleName = 'Microsoft.WinGet.DSC' Property = @{ }