From 50da177b9948a4d12565d1e5d1d5ab36407abe6c Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 08:00:12 -0500 Subject: [PATCH 01/10] (maint) Template specs - restrict to Windows only A couple of specs need to be restricted to Windows only. --- .../infrastructure.app/services/TemplateServiceSpecs.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/TemplateServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/TemplateServiceSpecs.cs index 1d4d0d63b8..936f754a9c 100644 --- a/src/chocolatey.tests/infrastructure.app/services/TemplateServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/TemplateServiceSpecs.cs @@ -364,6 +364,7 @@ public override void BeforeEachSpec() } [Fact] + [WindowsOnly] public void should_generate_all_files_and_directories() { because(); @@ -383,7 +384,8 @@ public void should_generate_all_files_and_directories() MockLogger.MessagesFor(LogLevel.Info).Last().ShouldEqual(string.Format(@"Successfully generated Bob package specification files{0} at 'c:\chocolatey\Bob'", Environment.NewLine)); } - [Fact] + [Fact] + [WindowsOnly] public void should_generate_all_files_and_directories_even_with_outputdirectory() { config.OutputDirectory = "c:\\packages"; From 72425326ccc5587db5618a73166e24833438f02d Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 14:34:28 -0500 Subject: [PATCH 02/10] (GH-755) Track more MSI information Look up MSI in cryptic GUID location to gather more information when those things may be empty in uninstaller keys. Add localpackage location to know where the cached MSI is located. --- .../domain/RegistryApplicationKey.cs | 1 + .../services/RegistryService.cs | 147 +++++++++++++++++- 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/src/chocolatey/infrastructure.app/domain/RegistryApplicationKey.cs b/src/chocolatey/infrastructure.app/domain/RegistryApplicationKey.cs index 5c36883024..f194ebe0ab 100644 --- a/src/chocolatey/infrastructure.app/domain/RegistryApplicationKey.cs +++ b/src/chocolatey/infrastructure.app/domain/RegistryApplicationKey.cs @@ -63,6 +63,7 @@ public class RegistryApplicationKey : IEquatable public bool NoRepair { get; set; } public string ReleaseType { get; set; } //hotfix, securityupdate, update rollup, servicepack public string ParentKeyName { get; set; } + public string LocalPackage { get; set; } /// /// Is an application listed in ARP (Programs and Features)? diff --git a/src/chocolatey/infrastructure.app/services/RegistryService.cs b/src/chocolatey/infrastructure.app/services/RegistryService.cs index d69805a368..e7e841ba30 100644 --- a/src/chocolatey/infrastructure.app/services/RegistryService.cs +++ b/src/chocolatey/infrastructure.app/services/RegistryService.cs @@ -16,12 +16,13 @@ namespace chocolatey.infrastructure.app.services { using System; - using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; + using System.Text; + using System.Text.RegularExpressions; using Microsoft.Win32; using domain; using filesystem; @@ -37,8 +38,10 @@ public sealed class RegistryService : IRegistryService private readonly IXmlService _xmlService; private readonly IFileSystem _fileSystem; private readonly bool _logOutput = false; + //public RegistryService() {} private const string UNINSTALLER_KEY_NAME = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + private const string UNINSTALLER_MSI_MACHINE_KEY_NAME = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData"; private const string USER_ENVIRONMENT_REGISTRY_KEY_NAME = "Environment"; private const string MACHINE_ENVIRONMENT_REGISTRY_KEY_NAME = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; @@ -213,22 +216,27 @@ public void evaluate_keys(RegistryKey key, Registry snapshot) appKey.InstallerType = InstallerType.Custom; } + if (appKey.InstallerType == InstallerType.Msi) + { + get_msi_information(appKey, key); + } + if (_logOutput) { - if (appKey.is_in_programs_and_features() && appKey.InstallerType == InstallerType.Unknown) - { + //if (appKey.is_in_programs_and_features() && appKey.InstallerType == InstallerType.Unknown) + //{ foreach (var name in key.GetValueNames()) { //var kind = key.GetValueKind(name); var value = key.GetValue(name); if (name.is_equal_to("QuietUninstallString") || name.is_equal_to("UninstallString")) { - Console.WriteLine("key - {0}|{1}={2}|Type detected={3}".format_with(key.Name, name, value.to_string(), appKey.InstallerType.to_string())); + Console.WriteLine("key - {0}|{1}={2}|Type detected={3}|install location={4}".format_with(key.Name, name, value.to_string(), appKey.InstallerType.to_string(),appKey.InstallLocation.to_string())); } //Console.WriteLine("key - {0}, name - {1}, kind - {2}, value - {3}".format_with(key.Name, name, kind, value.to_string())); } - } + //} } snapshot.RegistryKeys.Add(appKey); @@ -238,6 +246,133 @@ public void evaluate_keys(RegistryKey key, Registry snapshot) key.Dispose(); } + private int _componentLoopCount = 0; + + private void get_msi_information(RegistryApplicationKey appKey, RegistryKey key) + { + _componentLoopCount = 0; + + var userDataProductKeyId = get_msi_user_data_key(key.Name); + if (string.IsNullOrWhiteSpace(userDataProductKeyId)) return; + + var hklm = open_key(RegistryHive.LocalMachine, RegistryView.Default); + if (Environment.Is64BitOperatingSystem) + { + hklm = open_key(RegistryHive.LocalMachine, RegistryView.Registry64); + } + + FaultTolerance.try_catch_with_logging_exception( + () => + { + var msiRegistryKey = hklm.OpenSubKey(UNINSTALLER_MSI_MACHINE_KEY_NAME, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey); + if (msiRegistryKey == null) return; + + foreach (var subKeyName in msiRegistryKey.GetSubKeyNames()) + { + var msiProductKey = FaultTolerance.try_catch_with_logging_exception( + () => msiRegistryKey.OpenSubKey("{0}\\Products\\{1}\\InstallProperties".format_with(subKeyName, userDataProductKeyId), RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey), + "Failed to open subkey named '{0}' for '{1}', likely due to permissions".format_with(subKeyName, msiRegistryKey.Name), + logWarningInsteadOfError: true); + if (msiProductKey == null) continue; + + appKey.InstallLocation = set_if_empty(appKey.InstallLocation, msiProductKey.GetValue("InstallLocation").to_string()); + // informational + appKey.Publisher = set_if_empty(appKey.Publisher, msiProductKey.GetValue("Publisher").to_string()); + appKey.InstallDate = set_if_empty(appKey.InstallDate, msiProductKey.GetValue("InstallDate").to_string()); + appKey.InstallSource = set_if_empty(appKey.InstallSource, msiProductKey.GetValue("InstallSource").to_string()); + appKey.Language = set_if_empty(appKey.Language, msiProductKey.GetValue("Language").to_string()); + appKey.LocalPackage = set_if_empty(appKey.LocalPackage, msiProductKey.GetValue("LocalPackage").to_string()); + + // Version + appKey.DisplayVersion = set_if_empty(appKey.DisplayVersion, msiProductKey.GetValue("DisplayVersion").to_string()); + appKey.Version = set_if_empty(appKey.Version, msiProductKey.GetValue("Version").to_string()); + appKey.VersionMajor = set_if_empty(appKey.VersionMajor, msiProductKey.GetValue("VersionMajor").to_string()); + appKey.VersionMinor = set_if_empty(appKey.VersionMinor, msiProductKey.GetValue("VersionMinor").to_string()); + + // search components for install location if still empty + // the performance of this is very bad - without this the query is sub-second + // with this it takes about 15 seconds with around 200 apps installed + //if (string.IsNullOrWhiteSpace(appKey.InstallLocation) && !appKey.Publisher.contains("Microsoft")) + //{ + // var msiComponentsKey = FaultTolerance.try_catch_with_logging_exception( + // () => msiRegistryKey.OpenSubKey("{0}\\Components".format_with(subKeyName), RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey), + // "Failed to open subkey named '{0}' for '{1}', likely due to permissions".format_with(subKeyName, msiRegistryKey.Name), + // logWarningInsteadOfError: true); + // if (msiComponentsKey == null) continue; + + // foreach (var msiComponentKeyName in msiComponentsKey.GetSubKeyNames()) + // { + // var msiComponentKey = FaultTolerance.try_catch_with_logging_exception( + // () => msiComponentsKey.OpenSubKey(msiComponentKeyName, RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey), + // "Failed to open subkey named '{0}' for '{1}', likely due to permissions".format_with(subKeyName, msiRegistryKey.Name), + // logWarningInsteadOfError: true); + + // if (msiComponentKey.GetValueNames().Contains(userDataProductKeyId, StringComparer.OrdinalIgnoreCase)) + // { + // _componentLoopCount++; + // appKey.InstallLocation = set_if_empty(appKey.InstallLocation, get_install_location_estimate(msiComponentKey.GetValue(userDataProductKeyId).to_string())); + // if (!string.IsNullOrWhiteSpace(appKey.InstallLocation)) break; + // if (_componentLoopCount >= 10) break; + // } + // } + //} + } + }, + "Failed to open subkeys for '{0}', likely due to permissions".format_with(hklm.Name), + logWarningInsteadOfError: true); + } + + private string set_if_empty(string current, string proposed) + { + if (string.IsNullOrWhiteSpace(current)) return proposed; + + return current; + } + + private Regex _guidRegex = new Regex(@"\{(?\w*)\-(?\w*)\-(?\w*)\-(?\w*)\-(?\w*)\}", RegexOptions.Compiled); + private Regex _programFilesRegex = new Regex(@"(?\w)[\:\?]\\(?[Pp]rogram\s[Ff]iles|[Pp]rogram\s[Ff]iles\s\(x86\))\\(?:[Mm]icrosoft[^\\]*|[Cc]ommon\s[Ff]iles|IIS|MSBuild|[Rr]eference\s[Aa]ssemblies|[Ww]indows[^\\]*|(?[^\\]+))\\", RegexOptions.Compiled); + private StringBuilder _userDataKey = new StringBuilder(); + + private string get_install_location_estimate(string componentPath) + { + var match = _programFilesRegex.Match(componentPath); + if (!match.Success) return string.Empty; + if (string.IsNullOrWhiteSpace(match.Groups["Name"].Value)) return string.Empty; + + return "{0}:\\{1}\\{2}".format_with(match.Groups["Drive"].Value, match.Groups["ProgFiles"].Value, match.Groups["Name"].Value); + } + + private string get_msi_user_data_key(string name) + { + _userDataKey.Clear(); + var match = _guidRegex.Match(name); + if (!match.Success) return string.Empty; + + for (int i = 0; i < 3; i++) + { + var fullGroup = match.Groups["ReverseFull{0}".format_with(i + 1)]; + if (fullGroup != null) + { + _userDataKey.Append(fullGroup.Value.ToCharArray().Reverse().ToArray()); + } + } + for (int i = 0; i < 2; i++) + { + var pairsGroup = match.Groups["ReversePairs{0}".format_with(i + 1)]; + if (pairsGroup != null) + { + var pairValue = pairsGroup.Value; + for (int j = 0; j < pairValue.Length - 1; j++) + { + _userDataKey.Append("{1}{0}".format_with(pairValue[j], pairValue[j + 1])); + j++; + } + } + } + + return _userDataKey.to_string(); + } + public Registry get_installer_key_differences(Registry before, Registry after) { //var difference = after.RegistryKeys.Where(r => !before.RegistryKeys.Contains(r)).ToList(); @@ -368,7 +503,7 @@ public static GenericRegistryValue get_value(RegistryHiveType hive, string subKe value = FaultTolerance.try_catch_with_logging_exception( () => { - if (key.GetValueNames().Contains(registryValue,StringComparer.InvariantCultureIgnoreCase)) + if (key.GetValueNames().Contains(registryValue, StringComparer.InvariantCultureIgnoreCase)) { return new GenericRegistryValue { From 80cf926bb8e106cde993832d575f548fbb4b9e9b Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 15:27:50 -0500 Subject: [PATCH 03/10] (GH-689) Report install location When unpacking or installing software, determine the install location of that software. If the install was was unable to be found, report that as well but report that it was an installer if it was. --- .../helpers/functions/Get-ChocolateyUnzip.ps1 | 1 + .../Install-ChocolateyInstallPackage.ps1 | 2 ++ .../builders/ConfigurationBuilder.cs | 5 ++- .../services/ChocolateyPackageService.cs | 33 +++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/chocolatey.resources/helpers/functions/Get-ChocolateyUnzip.ps1 b/src/chocolatey.resources/helpers/functions/Get-ChocolateyUnzip.ps1 index 9dcd1a3faf..dc0f2a3e32 100644 --- a/src/chocolatey.resources/helpers/functions/Get-ChocolateyUnzip.ps1 +++ b/src/chocolatey.resources/helpers/functions/Get-ChocolateyUnzip.ps1 @@ -161,5 +161,6 @@ param( default { throw "7-Zip signalled an unknown error (code $exitCode)" } } + $env:ChocolateyPackageInstallLocation = $destination return $destination } diff --git a/src/chocolatey.resources/helpers/functions/Install-ChocolateyInstallPackage.ps1 b/src/chocolatey.resources/helpers/functions/Install-ChocolateyInstallPackage.ps1 index 6a6b4eedb1..488c5e0b26 100644 --- a/src/chocolatey.resources/helpers/functions/Install-ChocolateyInstallPackage.ps1 +++ b/src/chocolatey.resources/helpers/functions/Install-ChocolateyInstallPackage.ps1 @@ -82,6 +82,8 @@ param( Write-Warning "Unable to generate `'$ignoreFile`'" } + $env:ChocolateyInstallerType = $fileType + $additionalInstallArgs = $env:chocolateyInstallArguments; if ($additionalInstallArgs -eq $null) { $additionalInstallArgs = ''; diff --git a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs index c0eb902e64..598fabb861 100644 --- a/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs +++ b/src/chocolatey/infrastructure.app/builders/ConfigurationBuilder.cs @@ -439,6 +439,9 @@ private static void set_environment_options(ChocolateyConfiguration config) public static void set_environment_variables(ChocolateyConfiguration config) { + Environment.SetEnvironmentVariable("ChocolateyPackageInstallLocation", null); + Environment.SetEnvironmentVariable("ChocolateyInstallerType", null); + Environment.SetEnvironmentVariable(ApplicationParameters.ChocolateyInstallEnvironmentVariableName, ApplicationParameters.InstallLocation); Environment.SetEnvironmentVariable("CHOCOLATEY_VERSION", config.Information.ChocolateyVersion); Environment.SetEnvironmentVariable("CHOCOLATEY_VERSION_PRODUCT", config.Information.ChocolateyProductVersion); @@ -474,7 +477,7 @@ public static void set_environment_variables(ChocolateyConfiguration config) Environment.SetEnvironmentVariable("https_proxy", "{0}{1}".format_with(proxyCreds, config.Proxy.Location)); Environment.SetEnvironmentVariable("chocolateyProxyLocation", config.Proxy.Location); } - + if (config.Features.UsePowerShellHost) Environment.SetEnvironmentVariable("ChocolateyPowerShellHost", "true"); if (config.Force) Environment.SetEnvironmentVariable("ChocolateyForce", "true"); } diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index dfe0aa4246..ecde075b26 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -329,6 +329,19 @@ public void handle_package_result(PackageResult packageResult, ChocolateyConfigu handle_template_packages(config, packageResult); } + var toolsLocation = _fileSystem.combine_paths(Environment.GetEnvironmentVariable("ChocolateyToolsLocation"), packageResult.Name); + if (_fileSystem.directory_exists(toolsLocation)) + { + Environment.SetEnvironmentVariable("ChocolateyPackageInstallLocation", toolsLocation, EnvironmentVariableTarget.Process); + } + + + if (pkgInfo.RegistrySnapshot != null && pkgInfo.RegistrySnapshot.RegistryKeys.Any(k => !string.IsNullOrWhiteSpace(k.InstallLocation))) + { + var key = pkgInfo.RegistrySnapshot.RegistryKeys.FirstOrDefault(k => !string.IsNullOrWhiteSpace(k.InstallLocation)); + if (key != null) Environment.SetEnvironmentVariable("ChocolateyPackageInstallLocation", key.InstallLocation, EnvironmentVariableTarget.Process); + } + _packageInfoService.save_package_information(pkgInfo); ensure_bad_package_path_is_clean(config, packageResult); EventManager.publish(new HandlePackageResultCompletedMessage(packageResult, config, commandName)); @@ -346,6 +359,22 @@ public void handle_package_result(PackageResult packageResult, ChocolateyConfigu if (packageResult.Success) remove_pending(packageResult, config); this.Log().Info(ChocolateyLoggers.Important, " The {0} of {1} was successful.".format_with(commandName.to_string(), packageResult.Name)); + + var installLocation = Environment.GetEnvironmentVariable("ChocolateyPackageInstallLocation"); + var installerDetected = Environment.GetEnvironmentVariable("ChocolateyInstallerType"); + if (!string.IsNullOrWhiteSpace(installLocation)) + { + this.Log().Info(ChocolateyLoggers.Important, " Software installed to '{0}'".format_with(installLocation)); + } + else if (!string.IsNullOrWhiteSpace(installerDetected)) + { + this.Log().Info(ChocolateyLoggers.Important, @" Software installed as '{0}', install location is likely default.".format_with(installerDetected)); + } + else + { + this.Log().Info(ChocolateyLoggers.Important, @" Software install location not explicitly set, could be in package or + default install location if installer."); + } } public ConcurrentDictionary install_run(ChocolateyConfiguration config) @@ -851,6 +880,8 @@ private void handle_extension_packages(ChocolateyConfiguration config, PackageRe string logMessage = " Installed/updated {0} extensions.".format_with(extensionsFolderName); this.Log().Warn(logMessage); packageResult.Messages.Add(new ResultMessage(ResultType.Note, logMessage)); + + Environment.SetEnvironmentVariable("ChocolateyPackageInstallLocation", packageExtensionsInstallDirectory, EnvironmentVariableTarget.Process); } else { @@ -935,6 +966,8 @@ private void handle_template_packages(ChocolateyConfiguration config, PackageRes string logMessage = " Installed/updated {0} template.".format_with(templateFolderName); this.Log().Warn(logMessage); packageResult.Messages.Add(new ResultMessage(ResultType.Note, logMessage)); + + Environment.SetEnvironmentVariable("ChocolateyPackageInstallLocation", installTemplatePath, EnvironmentVariableTarget.Process); } else { From 036eb318d69e133138be5b29f308ade6fde694b3 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 15:32:36 -0500 Subject: [PATCH 04/10] (maint) add review note --- .../infrastructure.app/services/ChocolateyPackageService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index ecde075b26..b59aecb03a 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -314,6 +314,8 @@ public void handle_package_result(PackageResult packageResult, ChocolateyConfigu _filesService.ensure_compatible_file_attributes(packageResult, config); _configTransformService.run(packageResult, config); + + //review: is this a Windows only kind of thing? pkgInfo.FilesSnapshot = _filesService.capture_package_files(packageResult, config); if (packageResult.Success) _shimgenService.install(config, packageResult); From 5eaaca28f96bf266e7b5d410ee76d1fc875cd457 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 17:55:19 -0500 Subject: [PATCH 05/10] (GH-710) If timeout, inform user When prompting with a timeout, the user should know that they will only have so long to answer the question. Give them a heads up so they are prepared to answer. --- .../infrastructure/commandline/InteractivePrompt.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs b/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs index f245caa5f9..d501200255 100644 --- a/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs +++ b/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs @@ -75,12 +75,17 @@ public static string prompt_for_confirmation(string prompt, IEnumerable (name, value) => { throw new ApplicationException("Multiple choices have the same first letter. Please ensure you pass choices with different first letters."); }); } + if (timeoutInSeconds > 0) + { + "chocolatey".Log().Info(ChocolateyLoggers.Important, "For the question below, you have {0} seconds to make a selection.".format_with(timeoutInSeconds)); + } + if (shortPrompt) { Console.Write(prompt + "("); } - + "chocolatey".Log().Info(shortPrompt ? ChocolateyLoggers.LogFileOnly : ChocolateyLoggers.Important, prompt); int counter = 1; @@ -136,7 +141,7 @@ public static string prompt_for_confirmation(string prompt, IEnumerable if (!selectionFound) { - "chocolatey".Log().Error(ChocolateyLoggers.Important, "Your choice of '{0}' is not a valid selection.".format_with(selection.escape_curly_braces())); + "chocolatey".Log().Error(ChocolateyLoggers.Important, "Timeout or your choice of '{0}' is not a valid selection.".format_with(selection.escape_curly_braces())); if (requireAnswer) { "chocolatey".Log().Warn(ChocolateyLoggers.Important, "You must select an answer"); From 72f510dc98c2a739e4e22b2f04057ab5eff27b3f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 18:03:00 -0500 Subject: [PATCH 06/10] (GH-735) split package list from all operation Split getting the package list from setting "all" items, such as upgrade all. This allows querying the list in other operations. --- .../services/NugetService.cs | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index a3e9ee4854..554661d066 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -187,7 +187,7 @@ Package url } if (config.RegularOutput) this.Log().Debug(() => "--- End of List ---"); - if (config.RegularOutput) + if (config.RegularOutput && !config.QuietOutput) { this.Log().Warn(() => @"{0} packages {1}.".format_with(count, config.ListCommand.LocalOnly ? "installed" : "found")); } @@ -1190,25 +1190,40 @@ public void remove_installation_files(IPackage removedPackage, ChocolateyPackage } } + private IEnumerable get_all_intalled_packages(ChocolateyConfiguration config) + { + config.ListCommand.LocalOnly = true; + var sources = config.Sources; + config.Sources = ApplicationParameters.PackagesLocation; + var pre = config.Prerelease; + config.Prerelease = true; + var noop = config.Noop; + config.Noop = false; + var packageNames = config.PackageNames; + config.PackageNames = string.Empty; + var input = config.Input; + config.Input = string.Empty; + var quiet = config.QuietOutput; + config.QuietOutput = true; + + var installedPackages = list_run(config).ToList(); + + config.QuietOutput = quiet; + config.Input = input; + config.PackageNames = packageNames; + config.Noop = noop; + config.Prerelease = pre; + config.Sources = sources; + + return installedPackages; + } + private void set_package_names_if_all_is_specified(ChocolateyConfiguration config, Action customAction) { if (config.PackageNames.is_equal_to(ApplicationParameters.AllPackages)) { - config.ListCommand.LocalOnly = true; - var sources = config.Sources; - config.Sources = ApplicationParameters.PackagesLocation; - var pre = config.Prerelease; - config.Prerelease = true; - var noop = config.Noop; - config.Noop = false; - config.PackageNames = string.Empty; - var input = config.Input; - config.Input = string.Empty; - var quiet = config.QuietOutput; - config.QuietOutput = true; - - var packagesToUpdate = list_run(config).Select(p => p.Name).ToList(); - + var packagesToUpdate = get_all_intalled_packages(config).Select(p => p.Name).ToList(); + if (!string.IsNullOrWhiteSpace(config.UpgradeCommand.PackageNamesToSkip)) { var packagesToSkip = config.UpgradeCommand.PackageNamesToSkip @@ -1240,11 +1255,6 @@ private void set_package_names_if_all_is_specified(ChocolateyConfiguration confi } } - config.QuietOutput = quiet; - config.Input = input; - config.Noop = noop; - config.Prerelease = pre; - config.Sources = sources; config.PackageNames = packagesToUpdate.@join(ApplicationParameters.PackageNamesSeparator); if (customAction != null) customAction.Invoke(); From 5ff630eecf33740b1fc852684dd6b0a30e9f4603 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 18:05:40 -0500 Subject: [PATCH 07/10] (GH-455) Summary - log reason for warnings/errors When providing the summary of an install/upgrade/uninstall, there is a list of packages that had warnings and errors. If there is a warning or error message to go along with that, provide that as part of the summary. --- .../services/ChocolateyPackageService.cs | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index b59aecb03a..9c2df33a81 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -431,7 +431,8 @@ public ConcurrentDictionary install_run(ChocolateyConfigu this.Log().Warn(ChocolateyLoggers.Important, "Warnings:"); foreach (var warning in packageInstalls.Where(p => p.Value.Warning).or_empty_list_if_null()) { - this.Log().Warn(ChocolateyLoggers.Important, " - {0}{1}".format_with(warning.Value.Name, warning.Value.ExitCode != 0 ? " (exit code {0})".format_with(warning.Value.ExitCode) : string.Empty)); + var warningMessage = warning.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Warn); + this.Log().Warn(ChocolateyLoggers.Important, " - {0}{1}".format_with(warning.Value.Name, warningMessage != null ? " - {0}".format_with(warningMessage.Message) : string.Empty)); } } @@ -453,7 +454,11 @@ Please reboot at your earliest convenience. this.Log().Error("Failures:"); foreach (var failure in packageInstalls.Where(p => !p.Value.Success).or_empty_list_if_null()) { - this.Log().Error(" - {0}{1}".format_with(failure.Value.Name, failure.Value.ExitCode != 0 ? " (exit code {0})".format_with(failure.Value.ExitCode) : string.Empty)); + var errorMessage = failure.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Error); + this.Log().Error(" - {0}{1}{2}".format_with(failure.Value.Name, + failure.Value.ExitCode != 0 ? " (exited {0})".format_with(failure.Value.ExitCode) : string.Empty, + errorMessage != null ? " - {0}".format_with(errorMessage.Message) : string.Empty + )); } } @@ -653,7 +658,8 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu this.Log().Warn(ChocolateyLoggers.Important, "Warnings:"); foreach (var warning in packageUpgrades.Where(p => p.Value.Warning).or_empty_list_if_null()) { - this.Log().Warn(ChocolateyLoggers.Important, " - {0}{1}".format_with(warning.Value.Name, warning.Value.ExitCode != 0 ? " (exit code {0})".format_with(warning.Value.ExitCode) : string.Empty)); + var warningMessage = warning.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Warn); + this.Log().Warn(ChocolateyLoggers.Important, " - {0}{1}".format_with(warning.Value.Name, warningMessage != null ? " - {0}".format_with(warningMessage.Message) : string.Empty)); } } @@ -675,7 +681,11 @@ Please reboot at your earliest convenience. this.Log().Error("Failures:"); foreach (var failure in packageUpgrades.Where(p => !p.Value.Success).or_empty_list_if_null()) { - this.Log().Error(" - {0}{1}".format_with(failure.Value.Name, failure.Value.ExitCode != 0 ? " (exit code {0})".format_with(failure.Value.ExitCode) : string.Empty)); + var errorMessage = failure.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Error); + this.Log().Error(" - {0}{1}{2}".format_with(failure.Value.Name, + failure.Value.ExitCode != 0 ? " (exited {0})".format_with(failure.Value.ExitCode) : string.Empty, + errorMessage != null ? " - {0}".format_with(errorMessage.Message) : string.Empty + )); } } @@ -734,6 +744,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi get_environment_after(config, environmentBefore, out environmentChanges, out environmentRemovals); var uninstallFailures = packageUninstalls.Count(p => !p.Value.Success); + var uninstallWarnings = packageUninstalls.Count(p => p.Value.Warning); var rebootPackages = packageUninstalls.Count(p => new[] { 1641, 3010 }.Contains(p.Value.ExitCode)); this.Log().Warn(() => @"{0}{1} uninstalled {2}/{3} packages. {4} packages failed.{0} See the log for details ({5}).".format_with( Environment.NewLine, @@ -744,6 +755,16 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi _fileSystem.combine_paths(ApplicationParameters.LoggingLocation, ApplicationParameters.LoggingFile) )); + if (uninstallWarnings != 0) + { + this.Log().Warn(ChocolateyLoggers.Important, "Warnings:"); + foreach (var warning in packageUninstalls.Where(p => p.Value.Warning).or_empty_list_if_null()) + { + var warningMessage = warning.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Warn); + this.Log().Warn(ChocolateyLoggers.Important, " - {0}{1}".format_with(warning.Value.Name, warningMessage != null ? " - {0}".format_with(warningMessage.Message) : string.Empty)); + } + } + if (rebootPackages != 0) { this.Log().Warn(ChocolateyLoggers.Important, "Packages needing reboot:"); @@ -762,7 +783,11 @@ Please reboot at your earliest convenience. this.Log().Error("Failures"); foreach (var failure in packageUninstalls.Where(p => !p.Value.Success).or_empty_list_if_null()) { - this.Log().Error(" - {0}{1}".format_with(failure.Value.Name, failure.Value.ExitCode != 0 ? " (exit code {0})".format_with(failure.Value.ExitCode) : string.Empty)); + var errorMessage = failure.Value.Messages.FirstOrDefault(m => m.MessageType == ResultType.Error); + this.Log().Error(" - {0}{1}{2}".format_with(failure.Value.Name, + failure.Value.ExitCode != 0 ? " (exited {0})".format_with(failure.Value.ExitCode) : string.Empty, + errorMessage != null ? " - {0}".format_with(errorMessage.Message) : string.Empty + )); } } From 0987cb5b457ae67b6fc4232a43e9e03a221a89ef Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 18:10:47 -0500 Subject: [PATCH 08/10] (GH-735) Remove actual when uninstalling meta When uninstalling a meta package, a user may not realize they need to uninstall the actual package as well, such as a *.portable or *.intall. For instance, if a user uninstalls git package, they may not realize they have only removed the meta package and git.install and subsequently, Git for Windows are still installed. Provide a warning with a timeout offering to add the actual package to the list. If there is no response or the answer is no, add the actual package to the list of warnings for the package summary with an explanation on how to uninstall that package. --- .../services/NugetService.cs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 554661d066..734fcd9b45 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -1010,6 +1010,60 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi } }; + // if we are uninstalling a package and not forcing dependencies, + // look to see if the user is missing the actual package they meant + // to uninstall. + if (!config.ForceDependencies) + { + // if you find an install of an .install / .portable / .commandline, allow adding it to the list + var installedPackages = get_all_intalled_packages(config).Select(p => p.Name).ToList().@join(ApplicationParameters.PackageNamesSeparator); + foreach (var packageName in config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).or_empty_list_if_null()) + { + var installerExists = installedPackages.contains("{0}.install".format_with(packageName)); + var portableExists = installedPackages.contains("{0}.portable".format_with(packageName)); + var cmdLineExists = installedPackages.contains("{0}.commandline".format_with(packageName)); + if ((!config.PackageNames.contains("{0}.install".format_with(packageName)) + && !config.PackageNames.contains("{0}.portable".format_with(packageName)) + && !config.PackageNames.contains("{0}.commandline".format_with(packageName)) + ) + && (installerExists || portableExists || cmdLineExists) + ) + { + var actualPackageName = installerExists ? + "{0}.install".format_with(packageName) + : portableExists ? + "{0}.portable".format_with(packageName) + : "{0}.commandline".format_with(packageName); + + var timeoutInSeconds = config.PromptForConfirmation ? 0 : 20; + this.Log().Warn(@"You are uninstalling {0}, which is likely a metapackage for an + *.install/*.portable package that it installed + ({0} represents discoverability).".format_with(packageName)); + var selection = InteractivePrompt.prompt_for_confirmation( + "Would you like to uninstall {0} as well?".format_with(actualPackageName), + new[] { "yes", "no" }, + defaultChoice: null, + requireAnswer: false, + allowShortAnswer: true, + shortPrompt: true, + timeoutInSeconds: timeoutInSeconds + ); + + if (selection.is_equal_to("yes")) + { + config.PackageNames += ";{0}".format_with(actualPackageName); + } + else + { + var logMessage = "To finish removing {0}, please also run the command: `choco uninstall {1}`.".format_with(packageName, actualPackageName); + var actualPackageResult = packageUninstalls.GetOrAdd(actualPackageName, new PackageResult(actualPackageName, null, null)); + actualPackageResult.Messages.Add(new ResultMessage(ResultType.Warn, logMessage)); + actualPackageResult.Messages.Add(new ResultMessage(ResultType.Inconclusive, logMessage)); + } + } + } + } + set_package_names_if_all_is_specified(config, () => { // force remove the item, ignore the dependencies From d51e259d38312c28ea6e5954ebf7ca4311820699 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 18:11:13 -0500 Subject: [PATCH 09/10] (maint) formatting --- .../services/NugetService.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index 734fcd9b45..7cc039c2a2 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -116,7 +116,6 @@ public IEnumerable list_run(ChocolateyConfiguration config) if (config.RegularOutput) this.Log().Debug(() => "Running list with the following filter = '{0}'".format_with(config.Input)); if (config.RegularOutput) this.Log().Debug(() => "--- Start of List ---"); foreach (var pkg in NugetList.GetPackages(config, _nugetLogger)) - { var package = pkg; // for lamda access @@ -125,7 +124,7 @@ public IEnumerable list_run(ChocolateyConfiguration config) if (config.RegularOutput) { this.Log().Info(config.Verbose ? ChocolateyLoggers.Important : ChocolateyLoggers.Normal, () => "{0} {1}{2}{3}{4}".format_with( - package.Id, + package.Id, package.Version.to_string(), package.IsApproved ? " [Approved]" : string.Empty, package.IsDownloadCacheAvailable ? " Downloads cached for licensed users" : string.Empty, @@ -169,7 +168,7 @@ Package url package.DocsUrl != null && !string.IsNullOrWhiteSpace(package.DocsUrl.to_string()) ? "{0} Documentation: {1}".format_with(Environment.NewLine, package.DocsUrl.to_string()) : string.Empty, package.MailingListUrl != null && !string.IsNullOrWhiteSpace(package.MailingListUrl.to_string()) ? "{0} Mailing List: {1}".format_with(Environment.NewLine, package.MailingListUrl.to_string()) : string.Empty, package.BugTrackerUrl != null && !string.IsNullOrWhiteSpace(package.BugTrackerUrl.to_string()) ? "{0} Issues: {1}".format_with(Environment.NewLine, package.BugTrackerUrl.to_string()) : string.Empty, - package.Description.escape_curly_braces().Replace("\n ","\n").Replace("\n","\n ") + package.Description.escape_curly_braces().Replace("\n ", "\n").Replace("\n", "\n ") )); } else @@ -286,7 +285,7 @@ public void push_run(ChocolateyConfiguration config) if (config.Sources.is_equal_to(ApplicationParameters.ChocolateyCommunityFeedPushSource) && config.RegularOutput) { - this.Log().Warn(ChocolateyLoggers.Important, () => @" + this.Log().Warn(ChocolateyLoggers.Important, () => @" Your package may be subject to moderation. A moderator will review the package prior to acceptance. You should have received an email. If you @@ -488,10 +487,10 @@ public void remove_rollback_directory_if_exists(string packageName) rollbackDirectory = _fileSystem.get_full_path(rollbackDirectory); } - + if (string.IsNullOrWhiteSpace(rollbackDirectory) || !_fileSystem.directory_exists(rollbackDirectory)) return; if (!rollbackDirectory.StartsWith(ApplicationParameters.PackageBackupLocation) || rollbackDirectory.is_equal_to(ApplicationParameters.PackageBackupLocation)) return; - + FaultTolerance.try_catch_with_logging_exception( () => _fileSystem.delete_directory_if_exists(rollbackDirectory, recursive: true), "Attempted to remove '{0}' but had an error:".format_with(rollbackDirectory), @@ -668,7 +667,7 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu this.Log().Info("{0}|{1}|{2}|{3}".format_with(installedPackage.Id, installedPackage.Version, availablePackage.Version, isPinned.to_string().to_lower())); } } - + continue; } @@ -719,7 +718,7 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu var currentPackageResult = new PackageResult(installedPackage, get_install_directory(config, installedPackage)); beforeUpgradeAction(currentPackageResult); } - + backup_existing_version(config, installedPackage, pkgInfo); remove_shim_directors(config, installedPackage, pkgInfo); if (config.Force && (installedPackage.Version == availablePackage.Version)) @@ -913,10 +912,10 @@ private void remove_shim_directors(ChocolateyConfiguration config, IPackage inst } } - private void remove_cache_for_package( ChocolateyConfiguration config, IPackage installedPackage) + private void remove_cache_for_package(ChocolateyConfiguration config, IPackage installedPackage) { var cacheDirectory = _fileSystem.combine_paths(config.CacheLocation, installedPackage.Id, installedPackage.Version.to_string()); - + FaultTolerance.try_catch_with_logging_exception( () => _fileSystem.delete_directory_if_exists(cacheDirectory, recursive: true), "Unable to removed cached files"); @@ -936,7 +935,7 @@ private void remove_nuget_cache_for_package(IPackage installedPackage) if (_fileSystem.file_exists(nugetCachedFile)) { FaultTolerance.try_catch_with_logging_exception( - () => _fileSystem.delete_file(nugetCachedFile), + () => _fileSystem.delete_file(nugetCachedFile), "Unable to removed cached NuGet package file"); } } @@ -1118,10 +1117,10 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi choices.Add(allVersionsChoice); } - var selection = InteractivePrompt.prompt_for_confirmation("Which version of {0} would you like to uninstall?".format_with(packageName), - choices, - defaultChoice: null, - requireAnswer: true, + var selection = InteractivePrompt.prompt_for_confirmation("Which version of {0} would you like to uninstall?".format_with(packageName), + choices, + defaultChoice: null, + requireAnswer: true, allowShortAnswer: false); if (string.IsNullOrWhiteSpace(selection)) continue; @@ -1188,7 +1187,7 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi } } } - + return packageUninstalls; } @@ -1229,11 +1228,11 @@ public void remove_installation_files(IPackage removedPackage, ChocolateyPackage FaultTolerance.try_catch_with_logging_exception( () => _fileSystem.delete_file(file), "Error deleting file"); - } + } if (fileSnapshot.Checksum == ApplicationParameters.HashProviderFileLocked) { - this.Log().Warn(()=> "Snapshot for '{0}' was attempted when file was locked.{1} Please inspect and manually remove file{1} at '{2}'".format_with(_fileSystem.get_file_name(file), Environment.NewLine, _fileSystem.get_directory_name(file))); + this.Log().Warn(() => "Snapshot for '{0}' was attempted when file was locked.{1} Please inspect and manually remove file{1} at '{2}'".format_with(_fileSystem.get_file_name(file), Environment.NewLine, _fileSystem.get_directory_name(file))); } } } From f6e5c1db05176e79d8ddf781c69bf71ed163774a Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Mon, 30 May 2016 18:31:17 -0500 Subject: [PATCH 10/10] (doc) update CHANGELOG/nuspec --- CHANGELOG.md | 12 ++++++++++++ nuget/chocolatey/chocolatey.nuspec | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50330fdf08..e8d1660998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Provide PowerShell tab completion for Chocolatey - see [#412](https://github.com/chocolatey/choco/issues/412) * [Security] Sign the powershell scripts and assemblies - see [#501](https://github.com/chocolatey/choco/issues/501) * Add a `choco info` command to show info for one package - see [#644](https://github.com/chocolatey/choco/issues/644) + * Mark packages pending until install completes successfully - see [#198](https://github.com/chocolatey/choco/issues/198) * Resolve sources by name - see [#356](https://github.com/chocolatey/choco/issues/356) * Pro/Business - Ubiquitous Install Directory Switch - see [#258](https://github.com/chocolatey/choco/issues/258) * Pro/Business - Runtime Virus Scanning - see [virus scanning](https://chocolatey.org/docs/features-virus-check) @@ -91,6 +92,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * [POSH Host] Fix - PowerShell Host doesn't show colorization overrides - see [#674](https://github.com/chocolatey/choco/issues/674) * [POSH Host] Fix - $profile is empty string when installing packages - does not automatically install the ChocolateyProfile - see [#667](https://github.com/chocolatey/choco/issues/667) * [POSH Host] Fix - Getting LCID doesn't work properly with the built-in PowerShell - see [#741](https://github.com/chocolatey/choco/issues/741) + * [POSH Host] Fix - Host.Version should return actual PowerShell version - see [#708](https://github.com/chocolatey/choco/issues/708) * Fix - Verbose shows in output on debug switch - see [#611](https://github.com/chocolatey/choco/issues/611) * Fix - Get-ChocolateyUnzip captures files that don't belong to the package / Unzip should not do a full disk scan - see [#616](https://github.com/chocolatey/choco/issues/616) and [#155](https://github.com/chocolatey/choco/issues/155) * Fix - Package succeeds but software install silently fails when Install-ChocolateyInstallPackage has the wrong arguments - see [#629](https://github.com/chocolatey/choco/issues/629) @@ -111,6 +113,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Fix - Package params are also applied to dependent package - see [#733](https://github.com/chocolatey/choco/issues/733) * Fix - Use package name/version from environment, not parameters - see [#751](https://github.com/chocolatey/choco/issues/751) * Fix - Get-WebFileName Does Not Match on Invalid Characters - see [#753](https://github.com/chocolatey/choco/issues/753) + * Fix - `choco new` cannot introduce multistage folder hierarchy template - see [#706](https://github.com/chocolatey/choco/issues/706) ### IMPROVEMENTS @@ -149,6 +152,14 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Clean up any temp nuget folder actions after NuGet operations - see [#622](https://github.com/chocolatey/choco/issues/622) * Ensure Web Requests and Responses Do Not Timeout - make configurable - see [#732](https://github.com/chocolatey/choco/issues/732) * Combine timeout from push and execution timeout as one parameter - see [#752](https://github.com/chocolatey/choco/issues/752) + * Override autouninstaller / failonautouninstaller fail with switches for uninstall - see [#515](https://github.com/chocolatey/choco/issues/515) + * Offer to remove actual package (*.install/*.portable) when removing meta/virtual package - see [#735](https://github.com/chocolatey/choco/issues/735) + * Provide more info in package summary - see [#455](https://github.com/chocolatey/choco/issues/455) + * Report install location - see [#689](https://github.com/chocolatey/choco/issues/689) + * Track MSI Information Better - see [#755](https://github.com/chocolatey/choco/issues/755) + * Support for client certificates - see [#399](https://github.com/chocolatey/choco/issues/399) + * choco feature list formatting enhancements - see [#742](https://github.com/chocolatey/choco/issues/742) + * choco new --original-template - see [#737](https://github.com/chocolatey/choco/issues/737) * Pro/Business - Also check for license in User Profile location - see [#606](https://github.com/chocolatey/choco/issues/606) * Pro/Business - Set download cache information if available - see [#562](https://github.com/chocolatey/choco/issues/562) * Pro/Business - Allow commands to be added - see [#583](https://github.com/chocolatey/choco/issues/583) @@ -157,6 +168,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Pro/Business - Add switch to fail on invalid or missing license - see [#596](https://github.com/chocolatey/choco/issues/596) * Pro/Business - add ignore invalid switches/parameters - see [#586](https://github.com/chocolatey/choco/issues/586) * Pro/Business - Don't prompt to upload file for virus scanning if it is too large - see [#695](https://github.com/chocolatey/choco/issues/695) + * Pro/Business - add 'support' command - see [#745](https://github.com/chocolatey/choco/issues/745) * API - Add the ability to retrieve package count for a Source - see [#431](https://github.com/chocolatey/choco/issues/431) * API - Chocolatey Lib still marks vital package information as internal - see [#433](https://github.com/chocolatey/choco/issues/433) * API - Add paging to list command - see [#427](https://github.com/chocolatey/choco/issues/427) diff --git a/nuget/chocolatey/chocolatey.nuspec b/nuget/chocolatey/chocolatey.nuspec index 161b92288d..dd257a9b8b 100644 --- a/nuget/chocolatey/chocolatey.nuspec +++ b/nuget/chocolatey/chocolatey.nuspec @@ -111,6 +111,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Provide PowerShell tab completion for Chocolatey - see [#412](https://github.com/chocolatey/choco/issues/412) * [Security] Sign the powershell scripts and assemblies - see [#501](https://github.com/chocolatey/choco/issues/501) * Add a `choco info` command to show info for one package - see [#644](https://github.com/chocolatey/choco/issues/644) + * Mark packages pending until install completes successfully - see [#198](https://github.com/chocolatey/choco/issues/198) * Resolve sources by name - see [#356](https://github.com/chocolatey/choco/issues/356) * Pro/Business - Ubiquitous Install Directory Switch - see [#258](https://github.com/chocolatey/choco/issues/258) * Pro/Business - Runtime Virus Scanning - see [virus scanning](https://chocolatey.org/docs/features-virus-check) @@ -146,6 +147,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * [POSH Host] Fix - PowerShell Host doesn't show colorization overrides - see [#674](https://github.com/chocolatey/choco/issues/674) * [POSH Host] Fix - $profile is empty string when installing packages - does not automatically install the ChocolateyProfile - see [#667](https://github.com/chocolatey/choco/issues/667) * [POSH Host] Fix - Getting LCID doesn't work properly with the built-in PowerShell - see [#741](https://github.com/chocolatey/choco/issues/741) + * [POSH Host] Fix - Host.Version should return actual PowerShell version - see [#708](https://github.com/chocolatey/choco/issues/708) * Fix - Verbose shows in output on debug switch - see [#611](https://github.com/chocolatey/choco/issues/611) * Fix - Get-ChocolateyUnzip captures files that don't belong to the package / Unzip should not do a full disk scan - see [#616](https://github.com/chocolatey/choco/issues/616) and [#155](https://github.com/chocolatey/choco/issues/155) * Fix - Package succeeds but software install silently fails when Install-ChocolateyInstallPackage has the wrong arguments - see [#629](https://github.com/chocolatey/choco/issues/629) @@ -166,6 +168,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Fix - Package params are also applied to dependent package - see [#733](https://github.com/chocolatey/choco/issues/733) * Fix - Use package name/version from environment, not parameters - see [#751](https://github.com/chocolatey/choco/issues/751) * Fix - Get-WebFileName Does Not Match on Invalid Characters - see [#753](https://github.com/chocolatey/choco/issues/753) + * Fix - `choco new` cannot introduce multistage folder hierarchy template - see [#706](https://github.com/chocolatey/choco/issues/706) ### IMPROVEMENTS @@ -204,6 +207,14 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Clean up any temp nuget folder actions after NuGet operations - see [#622](https://github.com/chocolatey/choco/issues/622) * Ensure Web Requests and Responses Do Not Timeout - make configurable - see [#732](https://github.com/chocolatey/choco/issues/732) * Combine timeout from push and execution timeout as one parameter - see [#752](https://github.com/chocolatey/choco/issues/752) + * Override autouninstaller / failonautouninstaller fail with switches for uninstall - see [#515](https://github.com/chocolatey/choco/issues/515) + * Offer to remove actual package (*.install/*.portable) when removing meta/virtual package - see [#735](https://github.com/chocolatey/choco/issues/735) + * Provide more info in package summary - see [#455](https://github.com/chocolatey/choco/issues/455) + * Report install location - see [#689](https://github.com/chocolatey/choco/issues/689) + * Track MSI Information Better - see [#755](https://github.com/chocolatey/choco/issues/755) + * Support for client certificates - see [#399](https://github.com/chocolatey/choco/issues/399) + * choco feature list formatting enhancements - see [#742](https://github.com/chocolatey/choco/issues/742) + * choco new --original-template - see [#737](https://github.com/chocolatey/choco/issues/737) * Pro/Business - Also check for license in User Profile location - see [#606](https://github.com/chocolatey/choco/issues/606) * Pro/Business - Set download cache information if available - see [#562](https://github.com/chocolatey/choco/issues/562) * Pro/Business - Allow commands to be added - see [#583](https://github.com/chocolatey/choco/issues/583) @@ -212,6 +223,7 @@ If you need the previous behavior, be sure to disable the feature `usePackageExi * Pro/Business - Add switch to fail on invalid or missing license - see [#596](https://github.com/chocolatey/choco/issues/596) * Pro/Business - add ignore invalid switches/parameters - see [#586](https://github.com/chocolatey/choco/issues/586) * Pro/Business - Don't prompt to upload file for virus scanning if it is too large - see [#695](https://github.com/chocolatey/choco/issues/695) + * Pro/Business - add 'support' command - see [#745](https://github.com/chocolatey/choco/issues/745) * API - Add the ability to retrieve package count for a Source - see [#431](https://github.com/chocolatey/choco/issues/431) * API - Chocolatey Lib still marks vital package information as internal - see [#433](https://github.com/chocolatey/choco/issues/433) * API - Add paging to list command - see [#427](https://github.com/chocolatey/choco/issues/427)