diff --git a/src/chocolatey.sln b/src/chocolatey.sln index 2a44908f3f..6a55062e88 100644 --- a/src/chocolatey.sln +++ b/src/chocolatey.sln @@ -1,8 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 -MinimumVisualStudioVersion = 10.0.40219.1 +# Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "chocolatey.console", "chocolatey.console\chocolatey.console.csproj", "{E24E3386-244F-4404-9E6E-5B53818EA903}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "chocolatey.tests", "chocolatey.tests\chocolatey.tests.csproj", "{5C4C60F0-47B1-498E-ABF7-D315E1A94BC9}" @@ -42,7 +40,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "toplevel", "toplevel", "{18FEFF9F-7352-4230-8B4E-E8D8BE976095}" ProjectSection(SolutionItems) = preProject ..\CHANGELOG.md = ..\CHANGELOG.md - ..\COMMITTERS.md = ..\COMMITTERS.md ..\CONTRIBUTING.md = ..\CONTRIBUTING.md ..\README.md = ..\README.md EndProjectSection @@ -138,8 +135,8 @@ Global {5C4C60F0-47B1-498E-ABF7-D315E1A94BC9} = {7656D054-387D-409C-A9FB-62A44599AA77} {12ECAD4F-D463-4D72-A792-A4FCCFE9C456} = {7656D054-387D-409C-A9FB-62A44599AA77} {DAB29F30-149D-4005-A8D2-4FA56CF2784D} = {FB6236DD-17EF-4C36-943C-47792FBC3306} - {E5C987F8-6B95-4835-BBDD-A25C9D370BB9} = {DAB29F30-149D-4005-A8D2-4FA56CF2784D} {DD9689F3-1D2D-41AE-A672-063EE70C0C9A} = {FB6236DD-17EF-4C36-943C-47792FBC3306} + {E5C987F8-6B95-4835-BBDD-A25C9D370BB9} = {DAB29F30-149D-4005-A8D2-4FA56CF2784D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {998CAC46-A2B8-447C-B4CC-3B11DC35ED46} diff --git a/src/chocolatey/infrastructure.app/nuget/NugetList.cs b/src/chocolatey/infrastructure.app/nuget/NugetList.cs index 7cb5cea46e..946c20f4ad 100644 --- a/src/chocolatey/infrastructure.app/nuget/NugetList.cs +++ b/src/chocolatey/infrastructure.app/nuget/NugetList.cs @@ -47,15 +47,15 @@ private static IQueryable execute_package_search(ChocolateyConfigurati // 2. Does it support prerelease in a straight-forward way? // Choco previously dealt with this by taking the path of least resistance and manually filtering out and sort unwanted packages // This result in blocking operations that didn't let service based repositories, like OData, take care of heavy lifting on the server. - bool isRemote; + bool isServiceBased; var aggregateRepo = packageRepository as AggregateRepository; if (aggregateRepo != null) { - isRemote = aggregateRepo.Repositories.All(repo => repo is IServiceBasedRepository); + isServiceBased = aggregateRepo.Repositories.All(repo => repo is IServiceBasedRepository); } else { - isRemote = packageRepository is IServiceBasedRepository; + isServiceBased = packageRepository is IServiceBasedRepository; } IQueryable results = packageRepository.Search(searchTermLower, configuration.Prerelease); @@ -72,21 +72,21 @@ private static IQueryable execute_package_search(ChocolateyConfigurati if (configuration.ListCommand.ByIdOnly) { - results = isRemote ? + results = isServiceBased ? results.Where(p => p.Id.ToLower().Contains(searchTermLower)) : results.Where(p => p.Id.contains(searchTermLower, StringComparison.OrdinalIgnoreCase)); } if (configuration.ListCommand.ByTagOnly) { - results = isRemote + results = isServiceBased ? results.Where(p => p.Tags.Contains(searchTermLower)) : results.Where(p => p.Tags.contains(searchTermLower, StringComparison.InvariantCultureIgnoreCase)); } if (configuration.ListCommand.IdStartsWith) { - results = isRemote ? + results = isServiceBased ? results.Where(p => p.Id.ToLower().StartsWith(searchTermLower)) : results.Where(p => p.Id.StartsWith(searchTermLower, StringComparison.OrdinalIgnoreCase)); } @@ -108,7 +108,7 @@ private static IQueryable execute_package_search(ChocolateyConfigurati if (configuration.AllVersions || !string.IsNullOrWhiteSpace(configuration.Version)) { - if (isRemote) + if (isServiceBased) { return results.OrderBy(p => p.Id).ThenByDescending(p => p.Version); } @@ -127,7 +127,7 @@ private static IQueryable execute_package_search(ChocolateyConfigurati results = results.Where(p => p.IsLatestVersion); } - if (!isRemote) + if (!isServiceBased) { results = results diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index a6c742fe25..9f9ea1cc69 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -611,7 +611,7 @@ Output is package name | current version | available version | pinned? var output = config.RegularOutput; config.RegularOutput = false; - var oudatedPackages = _nugetService.upgrade_noop(config, null); + var oudatedPackages = _nugetService.get_outdated(config); config.RegularOutput = output; if (config.RegularOutput) diff --git a/src/chocolatey/infrastructure.app/services/INugetService.cs b/src/chocolatey/infrastructure.app/services/INugetService.cs index c10545d4db..c1fb95074a 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -16,9 +16,11 @@ namespace chocolatey.infrastructure.app.services { - using configuration; + using chocolatey.infrastructure.results; + using configuration; + using System.Collections.Concurrent; - public interface INugetService : ISourceRunner + public interface INugetService : ISourceRunner { /// /// Run pack in noop mode. @@ -49,5 +51,11 @@ public interface INugetService : ISourceRunner /// /// Name of the package. void remove_rollback_directory_if_exists(string packageName); + + /// + /// Get outdated packages + /// + /// The configuration. + ConcurrentDictionary get_outdated(ChocolateyConfiguration config); } } diff --git a/src/chocolatey/infrastructure.app/services/NugetService.cs b/src/chocolatey/infrastructure.app/services/NugetService.cs index ac52cf277b..60ff3f5a2c 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -1,13 +1,13 @@ // Copyright © 2017 - 2018 Chocolatey Software, Inc // Copyright © 2011 - 2017 RealDimensions Software, LLC -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. -// +// // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -336,7 +336,7 @@ public virtual void push_run(ChocolateyConfiguration config) NugetPush.push_package(config, _fileSystem.get_full_path(nupkgFilePath)); - if (config.RegularOutput && (config.Sources.is_equal_to(ApplicationParameters.ChocolateyCommunityFeedPushSource) ||config.Sources.is_equal_to(ApplicationParameters.ChocolateyCommunityFeedPushSourceOld))) + if (config.RegularOutput && (config.Sources.is_equal_to(ApplicationParameters.ChocolateyCommunityFeedPushSource) || config.Sources.is_equal_to(ApplicationParameters.ChocolateyCommunityFeedPushSourceOld))) { this.Log().Warn(ChocolateyLoggers.Important, () => @" @@ -413,7 +413,7 @@ public virtual ConcurrentDictionary install_run(Chocolate } } - // this is when someone points the source directly at a nupkg + // this is when someone points the source directly at a nupkg // e.g. -source c:\somelocation\somewhere\packagename.nupkg if (config.Sources.to_string().EndsWith(Constants.PackageExtension)) { @@ -434,6 +434,7 @@ public virtual ConcurrentDictionary install_run(Chocolate uninstallSuccessAction: null, addUninstallHandler: true); + bool repositoryIsServiceBased = repository_is_service_based(packageManager.SourceRepository); foreach (string packageName in packageNames.or_empty_list_if_null()) { @@ -466,17 +467,17 @@ public virtual ConcurrentDictionary install_run(Chocolate continue; } - IPackage availablePackage = packageManager.SourceRepository.FindPackage(packageName, version, config.Prerelease, allowUnlisted: false); + IPackage availablePackage = find_package(packageName, version, config, packageManager.SourceRepository, repositoryIsServiceBased); if (availablePackage == null) { var logMessage = @"{0} not installed. The package was not found with the source(s) listed. Source(s): '{1}' NOTE: When you specify explicit sources, it overrides default sources. If the package version is a prerelease and you didn't specify `--pre`, - the package may not be found.{2}{3}".format_with(packageName, config.Sources, string.IsNullOrWhiteSpace(config.Version) ? String.Empty : + the package may not be found.{2}{3}".format_with(packageName, config.Sources, string.IsNullOrWhiteSpace(config.Version) ? String.Empty : @" Version was specified as '{0}'. It is possible that version - does not exist for '{1}' at the source specified.".format_with(config.Version.to_string(), packageName), + does not exist for '{1}' at the source specified.".format_with(config.Version.to_string(), packageName), @" Please see https://chocolatey.org/docs/troubleshooting for more assistance."); @@ -604,6 +605,8 @@ public virtual ConcurrentDictionary upgrade_run(Chocolate uninstallSuccessAction: null, addUninstallHandler: false); + bool repositoryIsServiceBased = repository_is_service_based(packageManager.SourceRepository); + var configIgnoreDependencies = config.IgnoreDependencies; set_package_names_if_all_is_specified(config, () => { config.IgnoreDependencies = true; }); config.IgnoreDependencies = configIgnoreDependencies; @@ -677,13 +680,13 @@ public virtual ConcurrentDictionary upgrade_run(Chocolate config.Prerelease = true; } - IPackage availablePackage = packageManager.SourceRepository.FindPackage(packageName, version, config.Prerelease, allowUnlisted: false); + IPackage availablePackage = find_package(packageName, version, config, packageManager.SourceRepository, repositoryIsServiceBased); config.Prerelease = originalPrerelease; if (availablePackage == null) { if (config.Features.IgnoreUnfoundPackagesOnUpgradeOutdated) continue; - + string logMessage = "{0} was not found with the source(s) listed.{1} If you specified a particular version and are receiving this message, it is possible that the package name exists but the version does not.{1} Version: \"{2}\"; Source(s): \"{3}\"".format_with(packageName, Environment.NewLine, config.Version, config.Sources); var unfoundResult = packageInstalls.GetOrAdd(packageName, new PackageResult(packageName, version.to_string(), null)); @@ -857,6 +860,126 @@ public virtual ConcurrentDictionary upgrade_run(Chocolate return packageInstalls; } + public ConcurrentDictionary get_outdated(ChocolateyConfiguration config) + { + var packageManager = NugetCommon.GetPackageManager( + config, + _nugetLogger, + _packageDownloader, + installSuccessAction: null, + uninstallSuccessAction: null, + addUninstallHandler: false); + + var repository = packageManager.SourceRepository; + bool repositoryIsServiceBased = repository_is_service_based(repository); + + var outdatedPackages = new ConcurrentDictionary(); + + set_package_names_if_all_is_specified(config, () => { config.IgnoreDependencies = true; }); + var packageNames = config.PackageNames.Split(new[] { ApplicationParameters.PackageNamesSeparator }, StringSplitOptions.RemoveEmptyEntries).or_empty_list_if_null().ToList(); + + var originalConfig = config.deep_copy(); + + foreach (var packageName in packageNames) + { + // set original config back each time through + config = originalConfig; + + var installedPackage = packageManager.LocalRepository.FindPackage(packageName); + var pkgInfo = _packageInfoService.get_package_information(installedPackage); + bool isPinned = pkgInfo.IsPinned; + + // if the package is pinned and we are skipping pinned, + // move on quickly + if (isPinned && config.OutdatedCommand.IgnorePinned) + { + string pinnedLogMessage = "{0} is pinned. Skipping pinned package.".format_with(packageName); + var pinnedPackageResult = outdatedPackages.GetOrAdd(packageName, new PackageResult(installedPackage, _fileSystem.combine_paths(ApplicationParameters.PackagesLocation, installedPackage.Id))); + pinnedPackageResult.Messages.Add(new ResultMessage(ResultType.Debug, pinnedLogMessage)); + pinnedPackageResult.Messages.Add(new ResultMessage(ResultType.Inconclusive, pinnedLogMessage)); + + continue; + } + + if (installedPackage != null && !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion) && !config.UpgradeCommand.ExcludePrerelease) + { + // this is a prerelease - opt in for newer prereleases. + config.Prerelease = true; + } + + var latestPackage = find_package(packageName, null, config, repository, repositoryIsServiceBased); + + if (latestPackage == null) + { + if (config.Features.IgnoreUnfoundPackagesOnUpgradeOutdated) continue; + + string unfoundLogMessage = "{0} was not found with the source(s) listed.{1} Source(s): \"{2}\"".format_with(packageName, Environment.NewLine, config.Sources); + var unfoundResult = outdatedPackages.GetOrAdd(packageName, new PackageResult(installedPackage, _fileSystem.combine_paths(ApplicationParameters.PackagesLocation, installedPackage.Id))); + unfoundResult.Messages.Add(new ResultMessage(ResultType.Warn, unfoundLogMessage)); + unfoundResult.Messages.Add(new ResultMessage(ResultType.Inconclusive, unfoundLogMessage)); + + this.Log().Warn("{0}|{1}|{1}|{2}".format_with(installedPackage.Id, installedPackage.Version, isPinned.to_string().to_lower())); + continue; + } + + if (latestPackage.Version <= installedPackage.Version) continue; + + var packageResult = outdatedPackages.GetOrAdd(packageName, new PackageResult(latestPackage, _fileSystem.combine_paths(ApplicationParameters.PackagesLocation, latestPackage.Id))); + + string logMessage = "You have {0} v{1} installed. Version {2} is available based on your source(s).{3} Source(s): \"{4}\"".format_with(installedPackage.Id, installedPackage.Version, latestPackage.Version, Environment.NewLine, config.Sources); + packageResult.Messages.Add(new ResultMessage(ResultType.Note, logMessage)); + + this.Log().Info("{0}|{1}|{2}|{3}".format_with(installedPackage.Id, installedPackage.Version, latestPackage.Version, isPinned.to_string().to_lower())); + } + + return outdatedPackages; + } + + private IPackage find_package(string packageName, SemanticVersion version, ChocolateyConfiguration config, IPackageRepository repository, bool isRepositoryServiceBased) + { + // find the package based on version + if (version != null) return repository.FindPackage(packageName, version, config.Prerelease, allowUnlisted: false); + + IQueryable results = repository.GetPackages().Where(x => x.Id == packageName); + + if (config.Prerelease && repository.SupportsPrereleasePackages) + { + results = results.Where(p => p.IsAbsoluteLatestVersion); + } + else + { + results = results.Where(p => p.IsLatestVersion); + } + + if (!isRepositoryServiceBased) + { + results = results + .Where(PackageExtensions.IsListed) + .Where(p => config.Prerelease || p.IsReleaseVersion()) + .distinct_last(PackageEqualityComparer.Id, PackageComparer.Version) + .AsQueryable(); + } + + // get only one result, should be the latest + return results.ToList().OrderByDescending(x => x.Version).FirstOrDefault(); + } + + private bool repository_is_service_based(IPackageRepository repository) + { + bool isRemote; + var aggregateRepo = repository as AggregateRepository; + if (aggregateRepo != null) + { + isRemote = aggregateRepo.Repositories.All(repo => repo is IServiceBasedRepository); + } + else + { + isRemote = repository is IServiceBasedRepository; + } + + return isRemote; + } + /// /// Sets the configuration for the package upgrade /// @@ -1046,10 +1169,10 @@ public virtual void backup_changed_files(string packageInstallPath, ChocolateyCo /// /// Remove the shimgen director files from the package. - /// These are .gui/.ignore files that may have been created during the installation + /// These are .gui/.ignore files that may have been created during the installation /// process and won't be pulled by the nuget package replacement. - /// This usually happens when package maintainers haven't been very good about how - /// they create the files in the past (not using force with new-item typically throws + /// This usually happens when package maintainers haven't been very good about how + /// they create the files in the past (not using force with new-item typically throws /// an error if the file exists). /// /// The configuration. @@ -1084,10 +1207,10 @@ private void remove_cache_for_package(ChocolateyConfiguration config, IPackage i } /// - /// Remove NuGet cache of the package. + /// Remove NuGet cache of the package. /// Whether we use the cached file or not, NuGet always caches the package. - /// This is annoying with choco, but if you use both choco and NuGet, it can - /// cause hard to detect issues in NuGet when there is a NuGet package of the + /// This is annoying with choco, but if you use both choco and NuGet, it can + /// cause hard to detect issues in NuGet when there is a NuGet package of the /// same name with different contents. /// /// The installed package. @@ -1179,12 +1302,12 @@ public virtual ConcurrentDictionary uninstall_run(Chocola } }; - // if we are uninstalling a package and not forcing dependencies, + // 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 + // if you find an install of an .install / .portable / .commandline, allow adding it to the list var installedPackages = get_all_installed_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()) {