Skip to content

Commit

Permalink
Merge pull request #11066 from dependabot/dev/brettfo/nuget-end-to-en…
Browse files Browse the repository at this point in the history
…d-ignore

parse and honor `ignore-conditions` from job file
  • Loading branch information
randhircs authored Dec 6, 2024
2 parents 7aba5f2 + ac1d5f8 commit 42c8a09
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using NuGet.Versioning;

using NuGetUpdater.Core.Analyze;
using NuGetUpdater.Core.Run;
using NuGetUpdater.Core.Run.ApiModel;

using Xunit;

namespace NuGetUpdater.Core.Test.Run;

public class MiscellaneousTests
{
[Theory]
[MemberData(nameof(RequirementsFromIgnoredVersionsData))]
public void RequirementsFromIgnoredVersions(string dependencyName, Condition[] ignoreConditions, Requirement[] expectedRequirements)
{
var job = new Job()
{
Source = new()
{
Provider = "github",
Repo = "some/repo"
},
IgnoreConditions = ignoreConditions
};
var actualRequirements = RunWorker.GetIgnoredRequirementsForDependency(job, dependencyName);
var actualRequirementsStrings = string.Join("|", actualRequirements.Select(r => r.ToString()));
var expectedRequirementsStrings = string.Join("|", expectedRequirements.Select(r => r.ToString()));
Assert.Equal(expectedRequirementsStrings, actualRequirementsStrings);
}

public static IEnumerable<object?[]> RequirementsFromIgnoredVersionsData()
{
yield return
[
// dependencyName
"Some.Package",
// ignoredConditions
new Condition[]
{
new()
{
DependencyName = "SOME.PACKAGE",
VersionRequirement = Requirement.Parse("> 1.2.3")
},
new()
{
DependencyName = "some.package",
VersionRequirement = Requirement.Parse("<= 2.0.0")
},
new()
{
DependencyName = "Unrelated.Package",
VersionRequirement = Requirement.Parse("= 3.4.5")
}
},
// expectedRequirements
new Requirement[]
{
new IndividualRequirement(">", NuGetVersion.Parse("1.2.3")),
new IndividualRequirement("<=", NuGetVersion.Parse("2.0.0")),
}
];

// version requirement is null => ignore all
yield return
[
// dependencyName
"Some.Package",
// ignoredConditions
new Condition[]
{
new()
{
DependencyName = "Some.Package"
}
},
// expectedRequirements
new Requirement[]
{
new IndividualRequirement(">", NuGetVersion.Parse("0.0.0"))
}
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,48 @@ public void SerializeError()
Assert.Equal(expected, actual);
}

[Fact]
public void DeserializeJobIgnoreConditions()
{
var jobContent = """
{
"job": {
"package-manager": "nuget",
"source": {
"provider": "github",
"repo": "some-org/some-repo",
"directory": "specific-sdk"
},
"ignore-conditions": [
{
"dependency-name": "Package.1",
"source": "some-file",
"version-requirement": "> 1.2.3"
},
{
"dependency-name": "Package.2",
"updated-at": "2024-12-05T15:47:12Z"
}
]
}
}
""";
var jobWrapper = RunWorker.Deserialize(jobContent)!;
Assert.Equal(2, jobWrapper.Job.IgnoreConditions.Length);

Assert.Equal("Package.1", jobWrapper.Job.IgnoreConditions[0].DependencyName);
Assert.Equal("some-file", jobWrapper.Job.IgnoreConditions[0].Source);
Assert.Empty(jobWrapper.Job.IgnoreConditions[0].UpdateTypes);
Assert.Null(jobWrapper.Job.IgnoreConditions[0].UpdatedAt);
Assert.Equal("> 1.2.3", jobWrapper.Job.IgnoreConditions[0].VersionRequirement?.ToString());

Assert.Equal("Package.2", jobWrapper.Job.IgnoreConditions[1].DependencyName);
Assert.Null(jobWrapper.Job.IgnoreConditions[1].Source);
Assert.Empty(jobWrapper.Job.IgnoreConditions[1].UpdateTypes);
Assert.Equal(new DateTime(2024, 12, 5, 15, 47, 12), jobWrapper.Job.IgnoreConditions[1].UpdatedAt);
Assert.Null(jobWrapper.Job.IgnoreConditions[1].VersionRequirement);
}

private class CapturingTestLogger : ILogger
{
private readonly List<string> _messages = new();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace NuGetUpdater.Core.Analyze;

public class RequirementConverter : JsonConverter<Requirement>
{
public override Requirement? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return Requirement.Parse(reader.GetString()!);
}

public override void Write(Utf8JsonWriter writer, Requirement value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Text.Json.Serialization;

using NuGetUpdater.Core.Analyze;

namespace NuGetUpdater.Core.Run.ApiModel;

public sealed record Condition
{
[JsonPropertyName("dependency-name")]
public required string DependencyName { get; init; }
[JsonPropertyName("source")]
public string? Source { get; init; } = null;
[JsonPropertyName("update-types")]
public string[] UpdateTypes { get; init; } = [];
[JsonPropertyName("updated-at")]
public DateTime? UpdatedAt { get; init; } = null;
[JsonPropertyName("version-requirement")]
public Requirement? VersionRequirement { get; init; } = null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed record Job
public object[]? ExistingPullRequests { get; init; } = null;
public object[]? ExistingGroupPullRequests { get; init; } = null;
public Dictionary<string, object>? Experiments { get; init; } = null;
public object[]? IgnoreConditions { get; init; } = null;
public Condition[] IgnoreConditions { get; init; } = [];
public bool LockfileOnly { get; init; } = false;
public string? RequirementsUpdateStrategy { get; init; } = null;
public object[]? SecurityAdvisories { get; init; } = null;
Expand Down
25 changes: 23 additions & 2 deletions nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Net;
using System.Text;
using System.Text.Json;
Expand All @@ -21,7 +22,7 @@ public class RunWorker
{
PropertyNamingPolicy = JsonNamingPolicy.KebabCaseLower,
WriteIndented = true,
Converters = { new JsonStringEnumConverter() },
Converters = { new JsonStringEnumConverter(), new RequirementConverter() },
};

public RunWorker(IApiHandler apiHandler, IDiscoveryWorker discoverWorker, IAnalyzeWorker analyzeWorker, IUpdaterWorker updateWorker, ILogger logger)
Expand Down Expand Up @@ -173,12 +174,13 @@ async Task TrackOriginalContentsAsync(string directory, string fileName)
continue;
}

var ignoredVersions = GetIgnoredRequirementsForDependency(job, dependency.Name);
var dependencyInfo = new DependencyInfo()
{
Name = dependency.Name,
Version = dependency.Version!,
IsVulnerable = false,
IgnoredVersions = [],
IgnoredVersions = ignoredVersions,
Vulnerabilities = [],
};
var analysisResult = await _analyzeWorker.RunAsync(repoContentsPath.FullName, discoveryResult, dependencyInfo);
Expand Down Expand Up @@ -303,6 +305,25 @@ async Task AddUpdatedFileIfDifferentAsync(string directory, string fileName)
return result;
}

internal static ImmutableArray<Requirement> GetIgnoredRequirementsForDependency(Job job, string dependencyName)
{
var ignoreConditions = job.IgnoreConditions
.Where(c => c.DependencyName.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
.ToArray();
if (ignoreConditions.Length == 1 && ignoreConditions[0].VersionRequirement is null)
{
// if only one match with no version requirement, ignore all versions
return [Requirement.Parse("> 0.0.0")];
}

var ignoredVersions = ignoreConditions
.Select(c => c.VersionRequirement)
.Where(r => r is not null)
.Cast<Requirement>()
.ToImmutableArray();
return ignoredVersions;
}

internal static UpdatedDependencyList GetUpdatedDependencyListFromDiscovery(WorkspaceDiscoveryResult discoveryResult, string pathToContents)
{
string GetFullRepoPath(string path)
Expand Down

0 comments on commit 42c8a09

Please sign in to comment.