Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 2 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ Options:
-rt|--runtime <RUNTIME> Specifies an optional runtime identifier to be used during the restore target when projects are analyzed.
More information available on https://learn.microsoft.com/dotnet/core/rid-catalog.
-mv|--maximum-version <MAX_VERSION> The inclusive maximum package version to upgrade to. For example, a value of '8.0' would upgrade System.Text.Json 6.0.0 to the latest patch version of 8.0.x
-uod|--update-only-deprecated Update only deprecated packages. Can be used together with update-only-vulnerable
-uov|--update-only-vulnerable Update only vulnerable packages. Can be used together with update-only-deprecated
```

![Screenshot of dotnet-outdated](screenshot.png)
Expand Down
5 changes: 4 additions & 1 deletion 5 src/DotNetOutdated.Core/Services/INuGetPackageInfoService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NuGet.Frameworks;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
Expand All @@ -13,5 +14,7 @@ Task<IReadOnlyList<NuGetVersion>> GetAllVersions(string package, IEnumerable<Uri

Task<IReadOnlyList<NuGetVersion>> GetAllVersions(string package, IEnumerable<Uri> sources, bool includePrerelease, NuGetFramework targetFramework, string projectFilePath,
bool isDevelopmentDependency, int olderThanDays, bool ignoreFailedSources);
}
Task<IPackageSearchMetadata> GetSpecificVersion(string package, IEnumerable<Uri> sources, bool includePrerelease, NuGetFramework targetFramework,
string projectFilePath, NuGetVersion referencedVersion, bool ignoreFailedSources = false);
}
}
35 changes: 35 additions & 0 deletions 35 src/DotNetOutdated.Core/Services/NuGetPackageInfoService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace DotNetOutdated.Core.Services
{
using NuGet.Packaging.Core;
using System.Collections.Concurrent;

public sealed class NuGetPackageInfoService : INuGetPackageInfoService, IDisposable
Expand Down Expand Up @@ -164,5 +165,39 @@ public void Dispose()
{
_context?.Dispose();
}

public async Task<IPackageSearchMetadata> GetSpecificVersion(string package, IEnumerable<Uri> sources, bool includePrerelease, NuGetFramework targetFramework,
string projectFilePath, NuGetVersion referencedVersion, bool ignoreFailedSources = false)
{
foreach (var source in sources)
{
try
{
var metadata = await FindMetadataResourceForSource(source, projectFilePath, package).ConfigureAwait(false);
if (metadata != null)
{
var packageMetadata = await metadata.GetMetadataAsync(new PackageIdentity(package, referencedVersion), _context, NullLogger.Instance, CancellationToken.None);
if (packageMetadata != null)
{
return packageMetadata;
}
}
}
catch (HttpRequestException)
{
// Suppress HTTP errors when connecting to NuGet sources
}
catch (Exception ex)
{
if (!ignoreFailedSources)
{
continue;
}
// if the inner exception is NOT HttpRequestException, throw it
if (ex.InnerException != null && !(ex.InnerException is HttpRequestException)) throw;
}
}
return null;
}
}
}
30 changes: 28 additions & 2 deletions 30 src/DotNetOutdated/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Reflection;
Expand All @@ -31,12 +32,14 @@ namespace DotNetOutdated
internal class Program(
IFileSystem fileSystem,
IReporter reporter,
INuGetPackageInfoService packageInfoService,
INuGetPackageResolutionService nugetService,
IProjectAnalysisService projectAnalysisService,
IProjectDiscoveryService projectDiscoveryService,
IDotNetPackageService dotNetPackageService,
ICentralPackageVersionManagementService centralPackageVersionManagementService) : CommandBase
{
private readonly INuGetPackageInfoService nuGetPackageInfoService = packageInfoService;
private readonly IFileSystem _fileSystem = fileSystem;
private readonly IReporter _reporter = reporter;
private readonly INuGetPackageResolutionService _nugetService = nugetService;
Expand Down Expand Up @@ -137,7 +140,13 @@ internal class Program(
"For example, a value of '8.0' would upgrade System.Text.Json 6.0.0 to the latest patch version of 8.0.x",
ShortName = "mv", LongName = "maximum-version")]
public string MaxVersion { get; set; } = string.Empty;


[Option(CommandOptionType.NoValue, Description = "Update only deprecated packages. Can be used together with update-only-vulnerable", ShortName = "uod", LongName = "update-only-deprecated")]
public bool UpdateOnlyDeprecatedPackages { get; set; } = false;

[Option(CommandOptionType.NoValue, Description = "Update only vulnerable packages. Can be used together with update-only-deprecated", ShortName = "uov", LongName = "update-only-vulnerable")]
public bool UpdateOnlyVulnerablePackages { get; set; } = false;

public static int Main(string[] args)
{
using var services = new ServiceCollection()
Expand Down Expand Up @@ -198,7 +207,6 @@ await Parallel.ForEachAsync(projectPaths, async (path, _) =>
});

var projects = projectLists.SelectMany(p => p).ToList();

// Analyze the dependencies
var outdatedProjects = await AnalyzeDependencies(projects, console).ConfigureAwait(false);

Expand Down Expand Up @@ -552,6 +560,24 @@ private async Task AddOutdatedDependencyIfNeeded(
OlderThanDays,
IgnoreFailedSources).ConfigureAwait(false);
}

if (UpdateOnlyVulnerablePackages || UpdateOnlyDeprecatedPackages)
{
var currentNugetPackageMetadata = await nuGetPackageInfoService
.GetSpecificVersion(dependency.Name, project.Sources, false, targetFramework.Name, project.FilePath, referencedVersion);
if (UpdateOnlyVulnerablePackages && currentNugetPackageMetadata.Vulnerabilities != null)
{
outdatedDependencies.Add(new AnalyzedDependency(dependency, latestVersion));
}

var depMetadata = await currentNugetPackageMetadata.GetDeprecationMetadataAsync();
if (UpdateOnlyDeprecatedPackages && depMetadata!=null)
{
outdatedDependencies.Add(new AnalyzedDependency(dependency, latestVersion));
}

return;
}

if (referencedVersion == null || latestVersion == null || referencedVersion != latestVersion || IncludeUpToDate)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.IO.Packaging" Version="5.0.0" />
<PackageReference Include="CsvHelper" Version="30.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.IO.Packaging" Version="6.0.0" />
<PackageReference Include="CsvHelper" Version="30.0.0" />
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions 69 test/DotNetOutdated.Tests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,75 @@ public static void Can_Upgrade_Project_With_Maximum_Version()
}
}

[Theory]
[InlineData("update-only-deprecated", true)]
[InlineData("update-only-vulnerable", false)]
public static void UpdateOnlyDeprecated(string testProjectName, bool expectUpdate)
{
using var directory = TestSetup(testProjectName);

var outputPath = Path.Combine(directory.Path, "output.json");

var actual = Program.Main([directory.Path, "--update-only-deprecated", "--output", outputPath, "--output-format:json"]);
Assert.Equal(0, actual);
if (!expectUpdate && !File.Exists(outputPath))
{
return;
}

using var output = JsonDocument.Parse(File.ReadAllText(outputPath));

foreach (var project in output.RootElement.GetProperty("Projects").EnumerateArray())
{
foreach (var tfm in project.GetProperty("TargetFrameworks").EnumerateArray())
{
var updatedCount = tfm.GetProperty("Dependencies").EnumerateArray().Count();
Assert.Equal(1, updatedCount);
foreach (var dependency in tfm.GetProperty("Dependencies").EnumerateArray())
{
var latestVersionString = dependency.GetProperty("LatestVersion").GetString();
var resolvedVersionString = dependency.GetProperty("ResolvedVersion").GetString();
Assert.NotEqual(latestVersionString, resolvedVersionString);
}
}

}
}
[Theory]
[InlineData("update-only-deprecated", false)]
[InlineData("update-only-vulnerable", true)]
public static void UpdateOnlyVulnerable(string testProjectName, bool expectUpdate)
{
using var directory = TestSetup(testProjectName);

var outputPath = Path.Combine(directory.Path, "output.json");

var actual = Program.Main([directory.Path, "--update-only-vulnerable", "--output", outputPath, "--output-format:json"]);
Assert.Equal(0, actual);
if (!expectUpdate && !File.Exists(outputPath))
{
return;
}

using var output = JsonDocument.Parse(File.ReadAllText(outputPath));

foreach (var project in output.RootElement.GetProperty("Projects").EnumerateArray())
{
foreach (var tfm in project.GetProperty("TargetFrameworks").EnumerateArray())
{
var updatedCount = tfm.GetProperty("Dependencies").EnumerateArray().Count();
Assert.Equal(1, updatedCount);
foreach (var dependency in tfm.GetProperty("Dependencies").EnumerateArray())
{
var latestVersionString = dependency.GetProperty("LatestVersion").GetString();
var resolvedVersionString = dependency.GetProperty("ResolvedVersion").GetString();
Assert.NotEqual(latestVersionString, resolvedVersionString);
}
}

}
}

private static TemporaryDirectory TestSetup(string testProjectName)
{
var solutionRoot = typeof(EndToEndTests).Assembly
Expand Down
Morty Proxy This is a proxified and sanitized view of the page, visit original site.