diff --git a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs index a6375d27b5..3de42e8888 100644 --- a/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs +++ b/src/chocolatey/infrastructure.app/services/ChocolateyPackageService.cs @@ -581,7 +581,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 af3c7cf29e..73a589eeef 100644 --- a/src/chocolatey/infrastructure.app/services/INugetService.cs +++ b/src/chocolatey/infrastructure.app/services/INugetService.cs @@ -1,13 +1,13 @@ // Copyright © 2017 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. @@ -16,6 +16,8 @@ namespace chocolatey.infrastructure.app.services { + using System.Collections.Concurrent; + using chocolatey.infrastructure.results; using configuration; public interface INugetService : ISourceRunner @@ -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 c937ea8644..6c474934f4 100644 --- a/src/chocolatey/infrastructure.app/services/NugetService.cs +++ b/src/chocolatey/infrastructure.app/services/NugetService.cs @@ -1,13 +1,13 @@ // Copyright © 2017 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. @@ -413,7 +413,7 @@ public ConcurrentDictionary install_run(ChocolateyConfigu } } - // 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)) { @@ -668,7 +668,7 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu 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)); @@ -845,6 +845,109 @@ public ConcurrentDictionary upgrade_run(ChocolateyConfigu 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 isRemote = repository_is_remote(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(); + + foreach (var packageName in packageNames) + { + var installedPackage = packageManager.LocalRepository.FindPackage(packageName); + + var pkgInfo = _packageInfoService.get_package_information(installedPackage); + bool isPinned = pkgInfo.IsPinned; + + if (config.OutdatedCommand.IgnorePinned && isPinned) + { + 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; + } + + IQueryable results = repository.GetPackages().Where(x => x.Id == packageName); + + var isPrerelease = !string.IsNullOrWhiteSpace(installedPackage.Version.SpecialVersion); + + if (isPrerelease && repository.SupportsPrereleasePackages) + { + results = results.Where(p => p.IsAbsoluteLatestVersion); + } + else + { + results = results.Where(p => p.IsLatestVersion); + } + + if (!isRemote) + { + results = results + .Where(PackageExtensions.IsListed) + .Where(p => isPrerelease || p.IsReleaseVersion()) + .AsQueryable(); + } + + var latestPackage = results.ToList().OrderByDescending(x => x.Version).FirstOrDefault(); + + 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) + { + 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 bool repository_is_remote(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 /// @@ -1034,10 +1137,10 @@ public void backup_changed_files(string packageInstallPath, ChocolateyConfigurat /// /// 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. @@ -1072,10 +1175,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. @@ -1168,12 +1271,12 @@ public ConcurrentDictionary uninstall_run(ChocolateyConfi } }; - // 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()) {