dependabot-nuget 0.316.0 → 0.318.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/DependencyInfo.cs +3 -0
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +30 -1
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/FrameworkChecker/FrameworkCompatibilityService.cs +25 -1
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Condition.cs +13 -1
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/DependencyGroup.cs +29 -18
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/ApiModel/Job.cs +7 -9
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +6 -1
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +13 -1
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +18 -10
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +11 -16
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +4 -2
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +29 -13
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +25 -7
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +15 -8
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/SpecialImportsConditionPatcher.cs +15 -2
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +11 -3
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/VersionFinderTests.cs +39 -0
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/FrameworkChecker/FrameworkCompatibilityServiceFacts.cs +8 -11
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +108 -15
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +39 -0
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +2 -2
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +291 -0
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandlerTests.cs +311 -6
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandlerTests.cs +273 -0
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandlerTests.cs +307 -0
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +51 -1
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/SpecialFilePatcherTests.cs +25 -0
  29. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b42ebbf0a6556e3766516b2b59eec508929837779a8f9ca2042a4d4f7d629626
4
- data.tar.gz: abb2a60e86d368d9d4a0f09c6311fd7b73f2ac708c1ec73c05d92d1b9409b27d
3
+ metadata.gz: 59a38e30a99a2cfa01adea0a410d889454db7c97be7403dc961f3a657cef9057
4
+ data.tar.gz: 9cd7cf460715c6bcd81b7a879eb9e18410e6bb7a5e3312f6c97e491530377d77
5
5
  SHA512:
6
- metadata.gz: 5b266cf823214164397b8e9ed67e5f238364ab95074f33f5cacec47adac5a6cecf147a39ccdb11701e29d0de0ca675b3dabd4a330d75d8ff81dbf7b660c26807
7
- data.tar.gz: fa309cdd55f81b5507329def15d7a7e6c50b7668e8f1f886cc7c0e1498ac8209aab577d6ba617dbe7ecffda79f93be8d696908b692a07415b78c677905bb9fdb
6
+ metadata.gz: 973628f0b72b612b75538f4428d1a71dd98517aed42f9725353455cfbb80bc9da8b5c92688a83197e3651f01084dcd97859f2bf2921dfdafdf8d26ae3f32280c
7
+ data.tar.gz: 9a5fc49a9cb8a586cde53c7f472be221712aa11a61349be64feddb42ab174257d7cdffab7954f3f0ccfc78d07a380c75c4f9001902ffbdbbb9a4a6790e0d8c8a
@@ -1,5 +1,7 @@
1
1
  using System.Collections.Immutable;
2
2
 
3
+ using NuGetUpdater.Core.Run.ApiModel;
4
+
3
5
  namespace NuGetUpdater.Core.Analyze;
4
6
 
5
7
  public sealed record DependencyInfo
@@ -9,4 +11,5 @@ public sealed record DependencyInfo
9
11
  public required bool IsVulnerable { get; init; }
10
12
  public ImmutableArray<Requirement> IgnoredVersions { get; init; }
11
13
  public ImmutableArray<SecurityVulnerability> Vulnerabilities { get; init; }
14
+ public ImmutableArray<ConditionUpdateType> IgnoredUpdateTypes { get; init; } = [];
12
15
  }
@@ -10,6 +10,8 @@ using NuGet.Protocol;
10
10
  using NuGet.Protocol.Core.Types;
11
11
  using NuGet.Versioning;
12
12
 
13
+ using NuGetUpdater.Core.Run.ApiModel;
14
+
13
15
  namespace NuGetUpdater.Core.Analyze;
14
16
 
15
17
  internal static class VersionFinder
@@ -155,12 +157,39 @@ internal static class VersionFinder
155
157
  var isIgnoredVersion = dependencyInfo.IgnoredVersions.Any(i => i.IsSatisfiedBy(version));
156
158
  var isVulnerableVersion = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version));
157
159
  var isSafeVersion = !safeVersions.Any() || safeVersions.Any(s => s.IsSatisfiedBy(version));
160
+
161
+ var isIgnoredByType = false;
162
+ if (currentVersion is not null)
163
+ {
164
+ var isMajorBump = version.Major > currentVersion.Major;
165
+ var isMinorBump = version.Major == currentVersion.Major && version.Minor > currentVersion.Minor;
166
+ var isPatchBump = version.Major == currentVersion.Major && version.Minor == currentVersion.Minor && version.Patch > currentVersion.Patch;
167
+ foreach (var ignoreType in dependencyInfo.IgnoredUpdateTypes)
168
+ {
169
+ switch (ignoreType)
170
+ {
171
+ case ConditionUpdateType.SemVerPatch:
172
+ isIgnoredByType = isIgnoredByType || isPatchBump || isMinorBump || isMajorBump;
173
+ break;
174
+ case ConditionUpdateType.SemVerMinor:
175
+ isIgnoredByType = isIgnoredByType || isMinorBump || isMajorBump;
176
+ break;
177
+ case ConditionUpdateType.SemVerMajor:
178
+ isIgnoredByType = isIgnoredByType || isMajorBump;
179
+ break;
180
+ default:
181
+ break;
182
+ }
183
+ }
184
+ }
185
+
158
186
  return versionGreaterThanCurrent
159
187
  && rangeSatisfies
160
188
  && prereleaseTypeMatches
161
189
  && !isIgnoredVersion
162
190
  && !isVulnerableVersion
163
- && isSafeVersion;
191
+ && isSafeVersion
192
+ && !isIgnoredByType;
164
193
  };
165
194
  }
166
195
 
@@ -24,7 +24,7 @@ public class FrameworkCompatibilityService
24
24
 
25
25
  foreach (var packageFramework in packageFrameworks)
26
26
  {
27
- if (packageFrameworks == null || packageFramework.IsUnsupported || packageFramework.IsPCL)
27
+ if (packageFrameworks == null || packageFramework.IsUnsupported)
28
28
  {
29
29
  continue;
30
30
  }
@@ -79,6 +79,30 @@ public class FrameworkCompatibilityService
79
79
  }
80
80
  }
81
81
 
82
+ // portable profiles
83
+ var portableMappings = new DefaultPortableFrameworkMappings();
84
+ var portableFrameworks = portableMappings.ProfileFrameworks.ToDictionary(p => p.Key, p => p.Value);
85
+ foreach (var (profileNumber, frameworkRange) in portableMappings.CompatibilityMappings)
86
+ {
87
+ var profileFramework = new NuGetFramework(FrameworkConstants.FrameworkIdentifiers.Portable, new Version(0, 0, 0, 0), $"Profile{profileNumber}");
88
+ var compatibleFrameworks = new HashSet<NuGetFramework>();
89
+ matrix.Add(profileFramework, compatibleFrameworks);
90
+
91
+ foreach (var packageFramework in AllSupportedFrameworks)
92
+ {
93
+ if (frameworkRange.Satisfies(packageFramework))
94
+ {
95
+ foreach (var projectFramework in AllSupportedFrameworks)
96
+ {
97
+ if (CompatibilityProvider.IsCompatible(projectFramework, packageFramework))
98
+ {
99
+ compatibleFrameworks.Add(projectFramework);
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+
82
106
  return matrix;
83
107
  }
84
108
  }
@@ -11,9 +11,21 @@ public sealed record Condition
11
11
  [JsonPropertyName("source")]
12
12
  public string? Source { get; init; } = null;
13
13
  [JsonPropertyName("update-types")]
14
- public string[] UpdateTypes { get; init; } = [];
14
+ public ConditionUpdateType[]? UpdateTypes { get; init; } = null;
15
15
  [JsonPropertyName("updated-at")]
16
16
  public DateTime? UpdatedAt { get; init; } = null;
17
17
  [JsonPropertyName("version-requirement")]
18
18
  public Requirement? VersionRequirement { get; init; } = null;
19
19
  }
20
+
21
+ public enum ConditionUpdateType
22
+ {
23
+ [JsonStringEnumMemberName("version-update:semver-major")]
24
+ SemVerMajor,
25
+
26
+ [JsonStringEnumMemberName("version-update:semver-minor")]
27
+ SemVerMinor,
28
+
29
+ [JsonStringEnumMemberName("version-update:semver-patch")]
30
+ SemVerPatch,
31
+ }
@@ -1,5 +1,6 @@
1
1
  using System.Collections.Immutable;
2
2
  using System.IO.Enumeration;
3
+ using System.Text.Json;
3
4
 
4
5
  namespace NuGetUpdater.Core.Run.ApiModel;
5
6
 
@@ -32,33 +33,43 @@ public class GroupMatcher
32
33
 
33
34
  public static GroupMatcher FromRules(Dictionary<string, object> rules)
34
35
  {
35
- string[] patterns;
36
- if (rules.TryGetValue("patterns", out var patternsObject) &&
37
- patternsObject is string[] patternsArray)
36
+ var patterns = GetStringArray(rules, "patterns", ["*"]); // default to matching everything unless explicitly excluded
37
+ var excludePatterns = GetStringArray(rules, "exclude-patterns", []);
38
+
39
+ return new GroupMatcher()
38
40
  {
39
- patterns = patternsArray;
40
- }
41
- else
41
+ Patterns = patterns,
42
+ ExcludePatterns = excludePatterns,
43
+ };
44
+ }
45
+
46
+ private static ImmutableArray<string> GetStringArray(Dictionary<string, object> rules, string propertyName, ImmutableArray<string> defaultValue)
47
+ {
48
+ if (!rules.TryGetValue(propertyName, out var propertyObject))
42
49
  {
43
- patterns = ["*"]; // default to matching everything unless excluded below
50
+ return defaultValue;
44
51
  }
45
52
 
46
- string[] excludePatterns;
47
- if (rules.TryGetValue("exclude-patterns", out var excludePatternsObject) &&
48
- excludePatternsObject is string[] excludePatternsArray)
53
+ if (propertyObject is string[] stringArray)
49
54
  {
50
- excludePatterns = excludePatternsArray;
55
+ // shortcut for unit tests which directly supply the array
56
+ return [.. stringArray];
51
57
  }
52
- else
58
+
59
+ var patternsElements = new List<string>();
60
+ if (propertyObject is JsonElement element &&
61
+ element.ValueKind == JsonValueKind.Array)
53
62
  {
54
- excludePatterns = [];
63
+ foreach (var arrayElement in element.EnumerateArray())
64
+ {
65
+ if (arrayElement.ValueKind == JsonValueKind.String)
66
+ {
67
+ patternsElements.Add(arrayElement.GetString()!);
68
+ }
69
+ }
55
70
  }
56
71
 
57
- return new GroupMatcher()
58
- {
59
- Patterns = [.. patterns],
60
- ExcludePatterns = [.. excludePatterns],
61
- };
72
+ return [.. patternsElements];
62
73
  }
63
74
  }
64
75
 
@@ -100,15 +100,14 @@ public sealed record Job
100
100
  return existingPullRequest;
101
101
  }
102
102
 
103
- public bool IsDependencyIgnored(string dependencyName, string dependencyVersion)
103
+ public bool IsDependencyIgnoredByNameOnly(string dependencyName)
104
104
  {
105
- var versionsToIgnore = IgnoreConditions
106
- .Where(c => FileSystemName.MatchesSimpleExpression(c.DependencyName, dependencyName))
107
- .Select(c => c.VersionRequirement ?? Requirement.Parse(">= 0.0.0")) // no range means ignore everything
105
+ var packageNamesToIgnore = IgnoreConditions
106
+ .Where(c => (c.UpdateTypes ?? []).Length == 0 && c.VersionRequirement is null) // ignoring by name means there can't be any qualification
107
+ .Select(c => c.DependencyName)
108
108
  .ToArray();
109
- var parsedDependencyVersion = NuGetVersion.Parse(dependencyVersion);
110
- var isIgnored = versionsToIgnore
111
- .Any(r => r.IsSatisfiedBy(parsedDependencyVersion));
109
+ var isIgnored = packageNamesToIgnore
110
+ .Any(p => FileSystemName.MatchesSimpleExpression(p, dependencyName));
112
111
  return isIgnored;
113
112
  }
114
113
 
@@ -231,7 +230,6 @@ public class NullAsEmptyStringArrayConverter : JsonConverter<ImmutableArray<stri
231
230
 
232
231
  public override void Write(Utf8JsonWriter writer, ImmutableArray<string> value, JsonSerializerOptions options)
233
232
  {
234
- writer.WriteStartArray();
235
- writer.WriteEndArray();
233
+ JsonSerializer.Serialize(writer, value, options);
236
234
  }
237
235
  }
@@ -97,7 +97,12 @@ public class PullRequestTextGenerator
97
97
  var fromText = dependencySet.Versions.Length == 1 && dependencySet.Versions[0].OldVersion is not null
98
98
  ? $"from {dependencySet.Versions[0].OldVersion} "
99
99
  : string.Empty;
100
- return $"Bump{bumpSuffix} {dependencySet.Name} {fromText}to {string.Join(", ", dependencySet.Versions.Select(v => v.NewVersion.ToString()))}";
100
+ var newVersions = dependencySet.Versions
101
+ .Select(v => v.NewVersion)
102
+ .Distinct()
103
+ .OrderBy(v => v)
104
+ .ToArray();
105
+ return $"Bump{bumpSuffix} {dependencySet.Name} {fromText}to {string.Join(", ", newVersions.Select(v => v.ToString()))}";
101
106
  }
102
107
 
103
108
  private static DependencySet[] GetDependencySets(ImmutableArray<UpdateOperationBase> updateOperationsPerformed)
@@ -1,5 +1,6 @@
1
1
  using System.Collections.Immutable;
2
2
  using System.IO;
3
+ using System.IO.Enumeration;
3
4
  using System.Text;
4
5
  using System.Text.Json;
5
6
  using System.Text.Json.Serialization;
@@ -677,7 +678,12 @@ public class RunWorker
677
678
  var dependencyVersion = NuGetVersion.Parse(dependency.Version!);
678
679
  var securityAdvisories = job.SecurityAdvisories.Where(s => s.DependencyName.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).ToArray();
679
680
  var isVulnerable = securityAdvisories.Any(s => (s.AffectedVersions ?? []).Any(v => v.IsSatisfiedBy(dependencyVersion)));
680
- var ignoredVersions = GetIgnoredRequirementsForDependency(job, dependency.Name);
681
+ var ignoredVersions = job.IgnoreConditions
682
+ .Where(c => FileSystemName.MatchesSimpleExpression(c.DependencyName, dependency.Name))
683
+ .Select(c => c.VersionRequirement)
684
+ .Where(r => r is not null)
685
+ .Cast<Requirement>()
686
+ .ToImmutableArray();
681
687
  var vulnerabilities = securityAdvisories.Select(s => new SecurityVulnerability()
682
688
  {
683
689
  DependencyName = dependency.Name,
@@ -685,6 +691,11 @@ public class RunWorker
685
691
  VulnerableVersions = s.AffectedVersions ?? [],
686
692
  SafeVersions = s.SafeVersions.ToImmutableArray(),
687
693
  }).ToImmutableArray();
694
+ var ignoredUpdateTypes = job.IgnoreConditions
695
+ .Where(c => FileSystemName.MatchesSimpleExpression(c.DependencyName, dependency.Name))
696
+ .SelectMany(c => c.UpdateTypes ?? [])
697
+ .Distinct()
698
+ .ToImmutableArray();
688
699
  var dependencyInfo = new DependencyInfo()
689
700
  {
690
701
  Name = dependency.Name,
@@ -692,6 +703,7 @@ public class RunWorker
692
703
  IsVulnerable = isVulnerable,
693
704
  IgnoredVersions = ignoredVersions,
694
705
  Vulnerabilities = vulnerabilities,
706
+ IgnoredUpdateTypes = ignoredUpdateTypes,
695
707
  };
696
708
  return dependencyInfo;
697
709
  }
@@ -65,13 +65,29 @@ internal class CreateSecurityUpdatePullRequestHandler : IUpdateHandler
65
65
  foreach (var dependencyGroupToUpdate in groupedUpdateOperationsToPerform)
66
66
  {
67
67
  var dependencyName = dependencyGroupToUpdate.Key;
68
- var vulnerableDependenciesToUpdate = dependencyGroupToUpdate.Value
68
+ var vulnerableCandidateDependenciesToUpdate = dependencyGroupToUpdate.Value
69
69
  .Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
70
70
  .Where(set => set.Item3.IsVulnerable)
71
71
  .ToArray();
72
+ var vulnerableDependenciesToUpdate = vulnerableCandidateDependenciesToUpdate
73
+ .Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
74
+ .ToArray();
72
75
  if (vulnerableDependenciesToUpdate.Length == 0)
73
76
  {
74
- await apiHandler.RecordUpdateJobError(new SecurityUpdateNotNeeded(dependencyName));
77
+ // no update possible, check backwards to see if it's because of ignore conditions
78
+ var ignoredUpdates = vulnerableCandidateDependenciesToUpdate
79
+ .Where(set => set.Dependency.Name.Equals(dependencyName, StringComparison.OrdinalIgnoreCase))
80
+ .ToArray();
81
+ if (ignoredUpdates.Length > 0)
82
+ {
83
+ logger.Error($"Cannot update {dependencyName} because all versions are ignored.");
84
+ await apiHandler.RecordUpdateJobError(new SecurityUpdateIgnored(dependencyName));
85
+ }
86
+ else
87
+ {
88
+ await apiHandler.RecordUpdateJobError(new SecurityUpdateNotNeeded(dependencyName));
89
+ }
90
+
75
91
  continue;
76
92
  }
77
93
 
@@ -92,14 +108,6 @@ internal class CreateSecurityUpdatePullRequestHandler : IUpdateHandler
92
108
  continue;
93
109
  }
94
110
 
95
- if (dependencyInfo.IgnoredVersions.Any(ignored => ignored.IsSatisfiedBy(NuGetVersion.Parse(analysisResult.UpdatedVersion))) ||
96
- job.IsDependencyIgnored(dependency.Name, dependency.Version!))
97
- {
98
- logger.Error($"Cannot update {dependency.Name} for {projectPath} because all versions are ignored.");
99
- await apiHandler.RecordUpdateJobError(new SecurityUpdateIgnored(dependencyName));
100
- continue;
101
- }
102
-
103
111
  logger.Info($"Attempting update of {dependency.Name} from {dependency.Version} to {analysisResult.UpdatedVersion} for {projectPath}.");
104
112
  var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(projectPath);
105
113
  var updaterResult = await updaterWorker.RunAsync(repoContentsPath.FullName, projectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, dependency.IsTransitive);
@@ -57,6 +57,13 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
57
57
  {
58
58
  foreach (var group in job.DependencyGroups)
59
59
  {
60
+ var existingGroupPr = job.ExistingGroupPullRequests.FirstOrDefault(pr => pr.DependencyGroupName == group.Name);
61
+ if (existingGroupPr is not null)
62
+ {
63
+ logger.Info($"Existing pull request found for group {group.Name}. Skipping pull request creation.");
64
+ continue;
65
+ }
66
+
60
67
  logger.Info($"Starting update for group {group.Name}");
61
68
  var groupMatcher = group.GetGroupMatcher();
62
69
  var updateOperationsPerformed = new List<UpdateOperationBase>();
@@ -91,9 +98,9 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
91
98
  continue;
92
99
  }
93
100
 
94
- if (job.IsDependencyIgnored(dependency.Name, dependency.Version!))
101
+ if (job.IsDependencyIgnoredByNameOnly(dependency.Name))
95
102
  {
96
- logger.Info($"Skipping ignored dependency {dependency.Name}/{dependency.Version}.");
103
+ logger.Info($"Skipping ignored dependency {dependency.Name}.");
97
104
  continue;
98
105
  }
99
106
 
@@ -112,12 +119,6 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
112
119
  continue;
113
120
  }
114
121
 
115
- if (dependencyInfo.IgnoredVersions.Any(ignored => ignored.IsSatisfiedBy(NuGetVersion.Parse(analysisResult.UpdatedVersion))))
116
- {
117
- logger.Info($"Cannot update {dependency.Name} for {projectPath} because all versions are ignored.");
118
- continue;
119
- }
120
-
121
122
  var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(projectPath);
122
123
  var updaterResult = await updaterWorker.RunAsync(repoContentsPath.FullName, projectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, dependency.IsTransitive);
123
124
  if (updaterResult.Error is not null)
@@ -196,9 +197,9 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
196
197
  continue;
197
198
  }
198
199
 
199
- if (job.IsDependencyIgnored(dependency.Name, dependency.Version!))
200
+ if (job.IsDependencyIgnoredByNameOnly(dependency.Name))
200
201
  {
201
- logger.Info($"Skipping ignored dependency {dependency.Name}/{dependency.Version}.");
202
+ logger.Info($"Skipping ignored dependency {dependency.Name}.");
202
203
  continue;
203
204
  }
204
205
 
@@ -217,12 +218,6 @@ internal class GroupUpdateAllVersionsHandler : IUpdateHandler
217
218
  continue;
218
219
  }
219
220
 
220
- if (dependencyInfo.IgnoredVersions.Any(ignored => ignored.IsSatisfiedBy(NuGetVersion.Parse(analysisResult.UpdatedVersion))))
221
- {
222
- logger.Info($"Cannot update {dependency.Name} for {projectPath} because all versions are ignored.");
223
- continue;
224
- }
225
-
226
221
  var projectDiscovery = discoveryResult.GetProjectDiscoveryFromPath(projectPath);
227
222
  var updaterResult = await updaterWorker.RunAsync(repoContentsPath.FullName, projectPath, dependency.Name, dependency.Version!, analysisResult.UpdatedVersion, dependency.IsTransitive);
228
223
  if (updaterResult.Error is not null)
@@ -91,8 +91,8 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
91
91
  {
92
92
  var dependencyName = dependencyGroupToUpdate.Key;
93
93
  var relevantDependenciesToUpdate = dependencyGroupToUpdate.Value
94
+ .Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
94
95
  .Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
95
- .Where(set => !job.IsDependencyIgnored(set.Dependency.Name, set.Dependency.Version!))
96
96
  .ToArray();
97
97
 
98
98
  foreach (var (projectPath, dependency, dependencyInfo) in relevantDependenciesToUpdate)
@@ -145,7 +145,9 @@ internal class RefreshGroupUpdatePullRequestHandler : IUpdateHandler
145
145
  var rawDependencies = updatedDependencies.Select(d => new Dependency(d.Name, d.Version, DependencyType.Unknown)).ToArray();
146
146
  if (rawDependencies.Length == 0)
147
147
  {
148
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
148
+ var close = ClosePullRequest.WithUpdateNoLongerPossible(job);
149
+ logger.Info(close.GetReport());
150
+ await apiHandler.ClosePullRequest(close);
149
151
  continue;
150
152
  }
151
153
 
@@ -35,6 +35,7 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
35
35
  logger.ReportDiscovery(discoveryResult);
36
36
  if (discoveryResult.Error is not null)
37
37
  {
38
+ logger.Error($"Reporting error: {discoveryResult.Error.GetReport()}");
38
39
  await apiHandler.RecordUpdateJobError(discoveryResult.Error);
39
40
  return;
40
41
  }
@@ -55,7 +56,9 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
55
56
 
56
57
  if (groupedUpdateOperationsToPerform.Count == 0)
57
58
  {
58
- await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesRemoved(job));
59
+ var close = ClosePullRequest.WithDependenciesRemoved(job);
60
+ logger.Info(close.GetReport());
61
+ await apiHandler.ClosePullRequest(close);
59
62
  continue;
60
63
  }
61
64
 
@@ -65,7 +68,9 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
65
68
  .ToImmutableArray();
66
69
  if (missingDependencies.Length > 0)
67
70
  {
68
- await apiHandler.ClosePullRequest(ClosePullRequest.WithDependencyRemoved(job));
71
+ var close = ClosePullRequest.WithDependencyRemoved(job);
72
+ logger.Info(close.GetReport());
73
+ await apiHandler.ClosePullRequest(close);
69
74
  continue;
70
75
  }
71
76
 
@@ -75,14 +80,16 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
75
80
  {
76
81
  var dependencyName = dependencyGroupToUpdate.Key;
77
82
  var vulnerableDependenciesToUpdate = dependencyGroupToUpdate.Value
83
+ .Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
78
84
  .Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
79
- .Where(set => !job.IsDependencyIgnored(set.Dependency.Name, set.Dependency.Version!))
80
85
  .Where(set => set.Item3.IsVulnerable)
81
86
  .ToArray();
82
87
 
83
88
  if (vulnerableDependenciesToUpdate.Length < dependencyGroupToUpdate.Value.Length)
84
89
  {
85
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpToDate(job));
90
+ var close = ClosePullRequest.WithUpToDate(job);
91
+ logger.Info(close.GetReport());
92
+ await apiHandler.ClosePullRequest(close);
86
93
  return;
87
94
  }
88
95
 
@@ -99,8 +106,7 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
99
106
  if (!analysisResult.CanUpdate)
100
107
  {
101
108
  logger.Info($"No updatable version found for {dependency.Name} in {projectPath}.");
102
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
103
- return;
109
+ continue;
104
110
  }
105
111
 
106
112
  logger.Info($"Attempting update of {dependency.Name} from {dependency.Version} to {analysisResult.UpdatedVersion} for {projectPath}.");
@@ -115,13 +121,8 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
115
121
 
116
122
  if (updaterResult.UpdateOperations.Length == 0)
117
123
  {
118
- // nothing was done, but we may have already handled it
119
- var alreadyHandled = updatedDependencies.Where(updated => updated.Name == dependencyName && updated.Version == analysisResult.UpdatedVersion).Any();
120
- if (!alreadyHandled)
121
- {
122
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
123
- return;
124
- }
124
+ logger.Info($"No update operations performed for {dependency.Name}/{dependency.Version} in project {projectPath}.");
125
+ continue;
125
126
  }
126
127
 
127
128
  var patchedUpdateOperations = RunWorker.PatchInOldVersions(updaterResult.UpdateOperations, projectDiscovery);
@@ -138,6 +139,21 @@ internal class RefreshSecurityUpdatePullRequestHandler : IUpdateHandler
138
139
  }
139
140
  }
140
141
 
142
+ // ensure we did something
143
+ var updatesNotPerformed = jobDependencies
144
+ .Except(updatedDependencies.Select(d => d.Name), StringComparer.OrdinalIgnoreCase)
145
+ .Distinct()
146
+ .OrderBy(d => d, StringComparer.OrdinalIgnoreCase)
147
+ .ToArray();
148
+
149
+ if (updatesNotPerformed.Length > 0)
150
+ {
151
+ logger.Info($"No updates performed for: {string.Join(", ", updatesNotPerformed)}");
152
+ await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
153
+ continue;
154
+ }
155
+
156
+ // update or create
141
157
  var updatedDependencyFiles = await tracker.StopTrackingAsync();
142
158
  var rawDependencies = updatedDependencies.Select(d => new Dependency(d.Name, d.Version, DependencyType.Unknown)).ToArray();
143
159
  if (rawDependencies.Length > 0)
@@ -53,7 +53,9 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
53
53
 
54
54
  if (relevantUpdateOperationsToPerform.Count == 0)
55
55
  {
56
- await apiHandler.ClosePullRequest(ClosePullRequest.WithDependenciesRemoved(job));
56
+ var close = ClosePullRequest.WithDependenciesRemoved(job);
57
+ logger.Info(close.GetReport());
58
+ await apiHandler.ClosePullRequest(close);
57
59
  continue;
58
60
  }
59
61
 
@@ -63,7 +65,9 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
63
65
  .ToImmutableArray();
64
66
  if (missingDependencies.Length > 0)
65
67
  {
66
- await apiHandler.ClosePullRequest(ClosePullRequest.WithDependencyRemoved(job));
68
+ var close = ClosePullRequest.WithDependencyRemoved(job);
69
+ logger.Info(close.GetReport());
70
+ await apiHandler.ClosePullRequest(close);
67
71
  continue;
68
72
  }
69
73
 
@@ -75,8 +79,8 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
75
79
  {
76
80
  var dependencyName = dependencyUpdatesToPeform.Key;
77
81
  var dependencyInfosToUpdate = dependencyUpdatesToPeform.Value
82
+ .Where(o => !job.IsDependencyIgnoredByNameOnly(o.Dependency.Name))
78
83
  .Select(o => (o.ProjectPath, o.Dependency, RunWorker.GetDependencyInfo(job, o.Dependency)))
79
- .Where(set => !job.IsDependencyIgnored(set.Dependency.Name, set.Dependency.Version!))
80
84
  .ToArray();
81
85
 
82
86
  foreach (var (projectPath, dependency, dependencyInfo) in dependencyInfosToUpdate)
@@ -92,8 +96,7 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
92
96
  if (!analysisResult.CanUpdate)
93
97
  {
94
98
  logger.Info($"No updatable version found for {dependency.Name} in {projectPath}.");
95
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
96
- return;
99
+ continue;
97
100
  }
98
101
 
99
102
  logger.Info($"Attempting update of {dependency.Name} from {dependency.Version} to {analysisResult.UpdatedVersion} for {projectPath}.");
@@ -108,8 +111,8 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
108
111
 
109
112
  if (updaterResult.UpdateOperations.Length == 0)
110
113
  {
111
- await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
112
- return;
114
+ logger.Info($"No update operations performed for {dependency.Name}/{dependency.Version} in project {projectPath}.");
115
+ continue;
113
116
  }
114
117
 
115
118
  var patchedUpdateOperations = RunWorker.PatchInOldVersions(updaterResult.UpdateOperations, projectDiscovery);
@@ -126,6 +129,21 @@ internal class RefreshVersionUpdatePullRequestHandler : IUpdateHandler
126
129
  }
127
130
  }
128
131
 
132
+ // ensure we did something
133
+ var updatesNotPerformed = jobDependencies
134
+ .Except(updatedDependencies.Select(d => d.Name), StringComparer.OrdinalIgnoreCase)
135
+ .Distinct()
136
+ .OrderBy(d => d, StringComparer.OrdinalIgnoreCase)
137
+ .ToArray();
138
+
139
+ if (updatesNotPerformed.Length > 0)
140
+ {
141
+ logger.Info($"No updates performed for: {string.Join(", ", updatesNotPerformed)}");
142
+ await apiHandler.ClosePullRequest(ClosePullRequest.WithUpdateNoLongerPossible(job));
143
+ continue;
144
+ }
145
+
146
+ // update or create
129
147
  var updatedDependencyFiles = await tracker.StopTrackingAsync();
130
148
  var rawDependencies = updatedDependencies.Select(d => new Dependency(d.Name, d.Version, DependencyType.Unknown)).ToArray();
131
149
  if (rawDependencies.Length > 0)
@@ -201,6 +201,7 @@ internal static class PackageReferenceUpdater
201
201
  )
202
202
  {
203
203
  var topLevelNames = topLevelDependencies.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
204
+ var topLevelVersionStrings = topLevelDependencies.ToDictionary(d => d.Name, d => d.Version!, StringComparer.OrdinalIgnoreCase);
204
205
  var requestedVersions = requestedUpdates.ToDictionary(d => d.Name, d => NuGetVersion.Parse(d.Version!), StringComparer.OrdinalIgnoreCase);
205
206
  var resolvedVersions = resolvedDependencies
206
207
  .Select(d => (d.Name, NuGetVersion.TryParse(d.Version, out var version), version))
@@ -248,16 +249,22 @@ internal static class PackageReferenceUpdater
248
249
  }
249
250
  }
250
251
 
251
- if (rootPackageName is not null)
252
+ if (rootPackageName is not null && resolvedVersions.TryGetValue(rootPackageName, out var rootPackageVersion))
252
253
  {
253
- updateOperations.Add(new ParentUpdate()
254
+ // from a few lines up we've already confirmed that `rootPackageName` was a top-level dependency
255
+ var rootPackageVersionString = topLevelVersionStrings[rootPackageName];
256
+ if (NuGetVersion.TryParse(rootPackageVersionString, out var resolvedRootPackageVersion)
257
+ && rootPackageVersion > resolvedRootPackageVersion)
254
258
  {
255
- DependencyName = requestedDependencyName,
256
- NewVersion = requestedVersions[requestedDependencyName],
257
- UpdatedFiles = [],
258
- ParentDependencyName = rootPackageName,
259
- ParentNewVersion = packageVersions[rootPackageName],
260
- });
259
+ updateOperations.Add(new ParentUpdate()
260
+ {
261
+ DependencyName = requestedDependencyName,
262
+ NewVersion = requestedVersions[requestedDependencyName],
263
+ UpdatedFiles = [],
264
+ ParentDependencyName = rootPackageName,
265
+ ParentNewVersion = packageVersions[rootPackageName],
266
+ });
267
+ }
261
268
  }
262
269
  break;
263
270
  case (true, false):