dependabot-nuget 0.322.0 → 0.322.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Analyze.cs +1 -1
  3. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Discover.cs +10 -23
  4. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Run.cs +9 -0
  5. data/helpers/lib/NuGetUpdater/NuGetUpdater.Cli.Test/EntryPointTests.Update.cs +15 -232
  6. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Discover/SdkProjectDiscovery.cs +1 -154
  7. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/ExperimentsManager.cs +3 -12
  8. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/AzurePackageDetailFinder.cs +30 -0
  9. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGenerator.cs +237 -0
  10. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitHubPackageDetailFinder.cs +101 -0
  11. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/GitLabPackageDetailFinder.cs +107 -0
  12. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/HttpFetcher.cs +32 -0
  13. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IHttpFetcher.cs +30 -0
  14. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPackageDetailFinder.cs +47 -0
  15. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/IPullRequestBodyGenerator.cs +11 -0
  16. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestBodyGenerator/SimplePullRequestBodyGenerator.cs +15 -0
  17. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/PullRequestTextGenerator.cs +7 -3
  18. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunWorker.cs +3 -525
  19. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandler.cs +1 -1
  20. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/GroupUpdateAllVersionsHandler.cs +2 -2
  21. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandler.cs +1 -1
  22. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandler.cs +1 -1
  23. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandler.cs +1 -1
  24. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/FileWriterWorker.cs +1 -1
  25. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/FileWriters/XmlFileWriter.cs +10 -3
  26. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/PackageReferenceUpdater.cs +1 -856
  27. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Updater/UpdaterWorker.cs +16 -200
  28. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Utilities/MSBuildHelper.cs +6 -556
  29. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +9 -73
  30. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Clone/CloneWorkerTests.cs +2 -2
  31. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/DependencySolver/MSBuildDependencySolverTests.cs +1 -1
  32. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTestBase.cs +1 -20
  33. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.PackagesConfig.cs +3 -62
  34. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.Project.cs +13 -563
  35. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/DiscoveryWorkerTests.cs +20 -267
  36. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Discover/SdkProjectDiscoveryTests.cs +2 -2
  37. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/EndToEndTests.cs +131 -131
  38. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/MiscellaneousTests.cs +0 -203
  39. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/DetailedPullRequestBodyGeneratorTests.cs +871 -0
  40. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/IPackageDetailFinderTests.cs +28 -0
  41. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestBodyGenerator/TestHttpFetcher.cs +23 -0
  42. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestTextTests.cs +3 -2
  43. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/SerializationTests.cs +6 -12
  44. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/CreateSecurityUpdatePullRequestHandlerTests.cs +6 -6
  45. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/GroupUpdateAllVersionsHandlerTests.cs +18 -18
  46. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshGroupUpdatePullRequestHandlerTests.cs +15 -15
  47. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshSecurityUpdatePullRequestHandlerTests.cs +21 -21
  48. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/RefreshVersionUpdatePullRequestHandlerTests.cs +15 -15
  49. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdateHandlers/UpdateHandlersTestsBase.cs +1 -8
  50. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/FileWriterWorkerTests.cs +1 -1
  51. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/FileWriters/XmlFileWriterTests.cs +85 -0
  52. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/PackageReferenceUpdaterTests.cs +1 -159
  53. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/MSBuildHelperTests.cs +10 -660
  54. metadata +16 -10
  55. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Run/RunResult.cs +0 -13
  56. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/PullRequestMessageTests.cs +0 -296
  57. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/RunWorkerTests.cs +0 -3592
  58. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Run/UpdatePermittedAndMessageTests.cs +0 -457
  59. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Update/UpdateWorkerTests.DirsProj.cs +0 -378
  60. data/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Utilities/SdkPackageUpdaterHelperTests.cs +0 -175
@@ -0,0 +1,30 @@
1
+ using NuGet.Versioning;
2
+
3
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
4
+
5
+ internal class AzurePackageDetailFinder : IPackageDetailFinder
6
+ {
7
+ public string GetCompareUrlPath(string? oldTag, string? newTag)
8
+ {
9
+ // azure devops doesn't support direct tag listing so both parameters are likely to be null, but just in case, this is the correct url format
10
+ if (oldTag is not null && newTag is not null)
11
+ {
12
+ return $"branchCompare?baseVersion=GT{oldTag}&targetVersion=GT{newTag}";
13
+ }
14
+
15
+ if (newTag is not null)
16
+ {
17
+ return $"commits?itemVersion=GT{newTag}";
18
+ }
19
+
20
+ return "commits";
21
+ }
22
+
23
+ public Task<Dictionary<NuGetVersion, (string TagName, string? Details)>> GetReleaseDataForVersionsAsync(string repoName, NuGetVersion oldVersion, NuGetVersion newVersion)
24
+ {
25
+ // azure devops doesn't support direct tag listing
26
+ return Task.FromResult(new Dictionary<NuGetVersion, (string TagName, string? Details)>());
27
+ }
28
+
29
+ public string GetReleasesUrlPath() => "tags";
30
+ }
@@ -0,0 +1,237 @@
1
+ using System.Collections.Immutable;
2
+ using System.Text;
3
+ using System.Text.RegularExpressions;
4
+
5
+ using NuGet.Versioning;
6
+
7
+ using NuGetUpdater.Core.Run.ApiModel;
8
+ using NuGetUpdater.Core.Updater;
9
+
10
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
11
+
12
+ internal class DetailedPullRequestBodyGenerator : IPullRequestBodyGenerator, IDisposable
13
+ {
14
+ private readonly IHttpFetcher _httpFetcher;
15
+
16
+ public const string TruncatedMessage = "...\n\n_Description has been truncated_";
17
+
18
+ public DetailedPullRequestBodyGenerator(IHttpFetcher httpFetcher)
19
+ {
20
+ _httpFetcher = httpFetcher;
21
+ }
22
+
23
+ public void Dispose()
24
+ {
25
+ _httpFetcher.Dispose();
26
+ }
27
+
28
+ public async Task<string> GeneratePullRequestBodyTextAsync(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, ImmutableArray<ReportedDependency> updatedDependencies)
29
+ {
30
+ var sb = new StringBuilder();
31
+ var dedupedUpdateOperations = updateOperationsPerformed
32
+ .DistinctBy(u => $"{u.DependencyName}/{u.OldVersion}/{u.NewVersion}", StringComparer.OrdinalIgnoreCase)
33
+ .OrderBy(u => u.DependencyName, StringComparer.OrdinalIgnoreCase)
34
+ .ThenBy(u => u.OldVersion, NullableNuGetVersionComparer.Instance)
35
+ .ThenBy(u => u.NewVersion)
36
+ .ToImmutableArray();
37
+ foreach (var updateOperation in dedupedUpdateOperations)
38
+ {
39
+ if (sb.Length > 0)
40
+ {
41
+ // ensure a blank line between entries
42
+ sb.AppendLine();
43
+ }
44
+
45
+ var reportText = $"{updateOperation.GetReport(includeFileNames: false)}.";
46
+ var updatedDependency = updatedDependencies
47
+ .Where(d => d.Name.Equals(updateOperation.DependencyName, StringComparison.OrdinalIgnoreCase))
48
+ .Where(d => NuGetVersion.TryParse(d.Version, out var version) && version == updateOperation.NewVersion)
49
+ .FirstOrDefault();
50
+ var sourceUrl = (updatedDependency?.Requirements ?? [])
51
+ .Select(r => r.Source?.SourceUrl)
52
+ .FirstOrDefault(u => u is not null);
53
+ if (sourceUrl is null)
54
+ {
55
+ // no source url, just append the report text
56
+ sb.AppendLine(reportText);
57
+ }
58
+ else
59
+ {
60
+ // build detailed report
61
+ var packageNameIndex = reportText.IndexOf(updateOperation.DependencyName, StringComparison.OrdinalIgnoreCase);
62
+ if (packageNameIndex >= 0)
63
+ {
64
+ // link the package name
65
+ sb.AppendLine($"{reportText[..packageNameIndex]}[{updateOperation.DependencyName}]({sourceUrl}){reportText[(packageNameIndex + updateOperation.DependencyName.Length)..]}");
66
+ }
67
+ else
68
+ {
69
+ sb.AppendLine(reportText);
70
+ }
71
+
72
+ // more details
73
+ if (Uri.TryCreate(sourceUrl, UriKind.Absolute, out var uri))
74
+ {
75
+ IPackageDetailFinder? packageDetailFinder = uri.Host.ToLowerInvariant() switch
76
+ {
77
+ "dev.azure.com" => new AzurePackageDetailFinder(),
78
+ "github.com" => new GitHubPackageDetailFinder(_httpFetcher),
79
+ "gitlab.com" => new GitLabPackageDetailFinder(_httpFetcher),
80
+ var host when host.EndsWith(".visualstudio.com") => new AzurePackageDetailFinder(),
81
+ _ => null,
82
+ };
83
+
84
+ if (packageDetailFinder is not null &&
85
+ updateOperation.OldVersion is not null)
86
+ {
87
+ var repoName = uri.LocalPath.TrimStart('/');
88
+ var versionsAndDetails = await packageDetailFinder.GetReleaseDataForVersionsAsync(repoName, updateOperation.OldVersion, updateOperation.NewVersion);
89
+ var ordered = versionsAndDetails
90
+ .Where(kv => kv.Key != updateOperation.OldVersion)
91
+ .OrderByDescending(kv => kv.Key)
92
+ .ToList();
93
+
94
+ sb.AppendLine();
95
+ var detailsLineTemplate = IsHtmlSupported(job.Source.Provider)
96
+ ? "<details>\n<summary>{0}</summary>"
97
+ : "# {0}";
98
+ sb.AppendLine(string.Format(detailsLineTemplate, "Release notes"));
99
+
100
+ var releasesUrlPath = packageDetailFinder.GetReleasesUrlPath();
101
+ if (releasesUrlPath is not null)
102
+ {
103
+ sb.AppendLine();
104
+ sb.AppendLine($"_Sourced from [{updateOperation.DependencyName}'s releases]({sourceUrl}/{releasesUrlPath})._");
105
+ }
106
+
107
+ foreach (var (version, (tagName, body)) in ordered)
108
+ {
109
+ sb.AppendLine();
110
+ sb.AppendLine($"## {version}");
111
+ if (body is not null)
112
+ {
113
+ var truncatedBody = TruncateLineCount(body);
114
+ sb.AppendLine();
115
+ sb.AppendLine(truncatedBody);
116
+ }
117
+ }
118
+
119
+ if (ordered.Count == 0)
120
+ {
121
+ sb.AppendLine();
122
+ sb.AppendLine("No release notes found for this version range.");
123
+ }
124
+
125
+ string? oldTag = null;
126
+ if (versionsAndDetails.TryGetValue(updateOperation.OldVersion, out var oldVersionDetails))
127
+ {
128
+ oldTag = oldVersionDetails.TagName;
129
+ }
130
+
131
+ string? newTag = null;
132
+ if (versionsAndDetails.TryGetValue(updateOperation.NewVersion, out var newVersionDetails))
133
+ {
134
+ newTag = newVersionDetails.TagName;
135
+ }
136
+
137
+ var compareUrlPath = packageDetailFinder.GetCompareUrlPath(oldTag, newTag);
138
+ sb.AppendLine();
139
+ sb.AppendLine($"Commits viewable in [compare view]({sourceUrl}/{compareUrlPath}).");
140
+ if (IsHtmlSupported(job.Source.Provider))
141
+ {
142
+ sb.AppendLine("</details>");
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+
149
+ var prBody = sb.ToString().Replace("\r", "").TrimEnd();
150
+ var scrubbedBody = ScrubLinksAndMentions(prBody);
151
+ var truncatedPrBody = TruncateCharacterCount(scrubbedBody, GetMaxPrLength(job.Source.Provider));
152
+ return truncatedPrBody;
153
+ }
154
+
155
+ private static bool IsHtmlSupported(string sourceProvider)
156
+ {
157
+ return sourceProvider switch
158
+ {
159
+ "azure" or
160
+ "bitbucket" or
161
+ "codecommit" => false,
162
+ _ => true,
163
+ };
164
+ }
165
+
166
+ private static string TruncateLineCount(string text, int maxLines = 50)
167
+ {
168
+ var lines = text.Replace("\r", "").Split('\n');
169
+ if (lines.Length <= maxLines)
170
+ {
171
+ return text;
172
+ }
173
+
174
+ var truncatedText = string.Join("\n", lines.Take(maxLines)) + "\n ... (truncated)";
175
+ return truncatedText;
176
+ }
177
+
178
+ internal static int? GetMaxPrLength(string sourceProvider)
179
+ {
180
+ // values from, e.g., https://github.com/dependabot/dependabot-core/blob/5109e3e63925469ca0d6e7fa9449470d22c1847b/common/lib/dependabot/pull_request_creator/github.rb#L20
181
+ return sourceProvider switch
182
+ {
183
+ "azure" => 3999,
184
+ "bitbucket" => 262143,
185
+ "codecommit" => 10239,
186
+ "github" => 65535,
187
+ _ => null, // default case, no specific limit
188
+ };
189
+ }
190
+
191
+ private static string TruncateCharacterCount(string text, int? maxLength)
192
+ {
193
+ if (maxLength is null || text.Length <= maxLength)
194
+ {
195
+ return text;
196
+ }
197
+
198
+ var adjustedMaxLength = maxLength.Value - TruncatedMessage.Length;
199
+ var truncatedText = text[..adjustedMaxLength] + TruncatedMessage;
200
+ return truncatedText;
201
+ }
202
+
203
+ private static string ScrubLinksAndMentions(string text)
204
+ {
205
+ var mentionPattern = new Regex(@"(?<AtSymbol>@)(?<Name>[a-z0-9]+(-[a-z0-9]+)*)", RegexOptions.IgnoreCase);
206
+ var scrubbedOfMentions = mentionPattern.Replace(text, match => $"@\u200B{match.Groups["Name"].Value}");
207
+
208
+ var issuePattern = new Regex(@"(?<NumberSign>#)(?<Number>\d+)", RegexOptions.IgnoreCase);
209
+ var scrubbedOfIssues = issuePattern.Replace(scrubbedOfMentions, match => $"#\u200B{match.Groups["Number"].Value}");
210
+ return scrubbedOfIssues;
211
+ }
212
+
213
+ private class NullableNuGetVersionComparer : IComparer<NuGetVersion?>
214
+ {
215
+ public static readonly NullableNuGetVersionComparer Instance = new();
216
+
217
+ public int Compare(NuGetVersion? x, NuGetVersion? y)
218
+ {
219
+ if (x is null && y is null)
220
+ {
221
+ return 0;
222
+ }
223
+
224
+ if (x is null)
225
+ {
226
+ return -1;
227
+ }
228
+
229
+ if (y is null)
230
+ {
231
+ return 1;
232
+ }
233
+
234
+ return x.CompareTo(y);
235
+ }
236
+ }
237
+ }
@@ -0,0 +1,101 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGet.Versioning;
4
+
5
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
6
+
7
+ internal class GitHubPackageDetailFinder : IPackageDetailFinder
8
+ {
9
+ private readonly IHttpFetcher _httpFetcher;
10
+
11
+ public GitHubPackageDetailFinder(IHttpFetcher httpFetcher)
12
+ {
13
+ _httpFetcher = httpFetcher;
14
+ }
15
+
16
+ public string GetCompareUrlPath(string? oldTag, string? newTag)
17
+ {
18
+ if (oldTag is not null && newTag is not null)
19
+ {
20
+ return $"compare/{oldTag}...{newTag}";
21
+ }
22
+
23
+ if (newTag is not null)
24
+ {
25
+ return $"commits/{newTag}";
26
+ }
27
+
28
+ return "commits";
29
+ }
30
+
31
+ public async Task<Dictionary<NuGetVersion, (string TagName, string? Details)>> GetReleaseDataForVersionsAsync(string repoName, NuGetVersion oldVersion, NuGetVersion newVersion)
32
+ {
33
+ var result = new Dictionary<NuGetVersion, (string TagName, string? Details)>();
34
+ var url = $"https://api.github.com/repos/{repoName}/releases?per_page=100";
35
+ var jsonOption = await _httpFetcher.GetJsonElementAsync(url);
36
+ if (jsonOption is null)
37
+ {
38
+ return result;
39
+ }
40
+
41
+ var json = jsonOption.Value;
42
+ if (json.ValueKind != JsonValueKind.Array)
43
+ {
44
+ return result;
45
+ }
46
+
47
+ if (json.GetArrayLength() == 0)
48
+ {
49
+ return result;
50
+ }
51
+
52
+ foreach (var releaseObject in json.EnumerateArray())
53
+ {
54
+ if (releaseObject.ValueKind != JsonValueKind.Object)
55
+ {
56
+ continue;
57
+ }
58
+
59
+ // get release name
60
+ if (!releaseObject.TryGetProperty("name", out var releasenameElement) ||
61
+ releasenameElement.ValueKind != JsonValueKind.String)
62
+ {
63
+ continue;
64
+ }
65
+
66
+ var releaseName = releasenameElement.GetString()!;
67
+
68
+ // get tag name
69
+ if (!releaseObject.TryGetProperty("tag_name", out var tagNameElement) ||
70
+ tagNameElement.ValueKind != JsonValueKind.String)
71
+ {
72
+ continue;
73
+ }
74
+
75
+ var tagName = tagNameElement.GetString()!;
76
+
77
+ // find matching version
78
+ var correspondingVersion = IPackageDetailFinder.GetVersionFromNames(releaseName, tagName);
79
+ if (correspondingVersion is null)
80
+ {
81
+ continue;
82
+ }
83
+
84
+ if (correspondingVersion >= oldVersion && correspondingVersion <= newVersion)
85
+ {
86
+ if (!releaseObject.TryGetProperty("body", out var bodyElement) ||
87
+ bodyElement.ValueKind != JsonValueKind.String)
88
+ {
89
+ continue;
90
+ }
91
+
92
+ var body = bodyElement.GetString()!;
93
+ result[correspondingVersion] = (tagName, body);
94
+ }
95
+ }
96
+
97
+ return result;
98
+ }
99
+
100
+ public string GetReleasesUrlPath() => "releases";
101
+ }
@@ -0,0 +1,107 @@
1
+ using System.Text.Json;
2
+
3
+ using NuGet.Versioning;
4
+
5
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
6
+
7
+ internal class GitLabPackageDetailFinder : IPackageDetailFinder
8
+ {
9
+ private readonly IHttpFetcher _httpFetcher;
10
+
11
+ public GitLabPackageDetailFinder(IHttpFetcher httpFetcher)
12
+ {
13
+ _httpFetcher = httpFetcher;
14
+ }
15
+
16
+ public string GetCompareUrlPath(string? oldTag, string? newTag)
17
+ {
18
+ if (oldTag is not null && newTag is not null)
19
+ {
20
+ return $"-/compare/{oldTag}...{newTag}";
21
+ }
22
+
23
+ if (newTag is not null)
24
+ {
25
+ return $"-/commits/{newTag}";
26
+ }
27
+
28
+ return "-/commits";
29
+ }
30
+
31
+ public async Task<Dictionary<NuGetVersion, (string TagName, string? Details)>> GetReleaseDataForVersionsAsync(string repoName, NuGetVersion oldVersion, NuGetVersion newVersion)
32
+ {
33
+ var result = new Dictionary<NuGetVersion, (string TagName, string? Details)>();
34
+ var url = $"https://gitlab.com/api/v4/projects/{Uri.EscapeDataString(repoName)}/repository/tags";
35
+ var jsonOption = await _httpFetcher.GetJsonElementAsync(url);
36
+ if (jsonOption is null)
37
+ {
38
+ return result;
39
+ }
40
+
41
+ var json = jsonOption.Value;
42
+ if (json.ValueKind != JsonValueKind.Array)
43
+ {
44
+ return result;
45
+ }
46
+
47
+ if (json.GetArrayLength() == 0)
48
+ {
49
+ return result;
50
+ }
51
+
52
+ foreach (var responseObject in json.EnumerateArray())
53
+ {
54
+ if (responseObject.ValueKind != JsonValueKind.Object)
55
+ {
56
+ continue;
57
+ }
58
+
59
+ // get release name
60
+ if (!responseObject.TryGetProperty("name", out var releaseNameElement) ||
61
+ releaseNameElement.ValueKind != JsonValueKind.String)
62
+ {
63
+ continue;
64
+ }
65
+
66
+ var releaseName = releaseNameElement.GetString()!;
67
+
68
+ // get release info
69
+ string? tagName = null;
70
+ string? description = null;
71
+ if (responseObject.TryGetProperty("release", out var releaseObject) &&
72
+ releaseObject.ValueKind == JsonValueKind.Object)
73
+ {
74
+ if (releaseObject.TryGetProperty("tag_name", out var tagNameElement) &&
75
+ tagNameElement.ValueKind == JsonValueKind.String)
76
+ {
77
+ tagName = tagNameElement.GetString()!;
78
+ }
79
+
80
+ if (releaseObject.TryGetProperty("description", out var descriptionElement) &&
81
+ descriptionElement.ValueKind == JsonValueKind.String)
82
+ {
83
+ description = descriptionElement.GetString();
84
+ }
85
+ }
86
+
87
+ // find matching version
88
+ var correspondingVersion = IPackageDetailFinder.GetVersionFromNames(releaseName, tagName);
89
+ if (correspondingVersion is null)
90
+ {
91
+ continue;
92
+ }
93
+
94
+ var resultTag = tagName ?? releaseName;
95
+ if (resultTag is not null &&
96
+ correspondingVersion >= oldVersion &&
97
+ correspondingVersion <= newVersion)
98
+ {
99
+ result[correspondingVersion] = (resultTag, description);
100
+ }
101
+ }
102
+
103
+ return result;
104
+ }
105
+
106
+ public string GetReleasesUrlPath() => "-/releases";
107
+ }
@@ -0,0 +1,32 @@
1
+ using System.Net.Http.Headers;
2
+
3
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
4
+
5
+ internal class HttpFetcher : IHttpFetcher
6
+ {
7
+ private readonly HttpClient _httpClient;
8
+
9
+ public HttpFetcher()
10
+ {
11
+ _httpClient = new HttpClient();
12
+ }
13
+
14
+ public void Dispose()
15
+ {
16
+ _httpClient.Dispose();
17
+ }
18
+
19
+ public async Task<string?> GetStringAsync(string url)
20
+ {
21
+ var request = new HttpRequestMessage(HttpMethod.Get, url);
22
+ request.Headers.UserAgent.Add(new ProductInfoHeaderValue("dependabot-core", null));
23
+ var response = await _httpClient.SendAsync(request);
24
+ if (!response.IsSuccessStatusCode)
25
+ {
26
+ return null;
27
+ }
28
+
29
+ var result = await response.Content.ReadAsStringAsync();
30
+ return result;
31
+ }
32
+ }
@@ -0,0 +1,30 @@
1
+ using System.Text.Json;
2
+
3
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
4
+
5
+ internal interface IHttpFetcher : IDisposable
6
+ {
7
+ Task<string?> GetStringAsync(string url);
8
+ }
9
+
10
+ internal static class IHttpFetcherExtensions
11
+ {
12
+ public static async Task<JsonElement?> GetJsonElementAsync(this IHttpFetcher fetcher, string url)
13
+ {
14
+ var jsonString = await fetcher.GetStringAsync(url);
15
+ if (jsonString is null)
16
+ {
17
+ return null;
18
+ }
19
+
20
+ try
21
+ {
22
+ var json = JsonSerializer.Deserialize<JsonElement>(jsonString);
23
+ return json;
24
+ }
25
+ catch (JsonException)
26
+ {
27
+ return null;
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,47 @@
1
+ using NuGet.Versioning;
2
+
3
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
4
+
5
+ internal interface IPackageDetailFinder
6
+ {
7
+ string GetReleasesUrlPath();
8
+ string GetCompareUrlPath(string? oldTag, string? newTag);
9
+ Task<Dictionary<NuGetVersion, (string TagName, string? Details)>> GetReleaseDataForVersionsAsync(string repoName, NuGetVersion oldVersion, NuGetVersion newVersion);
10
+
11
+ internal static NuGetVersion? GetVersionFromNames(string? releaseName, string? tagName)
12
+ {
13
+ var prefixesToTrim = new[]
14
+ {
15
+ "version-",
16
+ "version.",
17
+ "version ",
18
+ "version",
19
+ "v-",
20
+ "v.",
21
+ "v ",
22
+ "v",
23
+ };
24
+ foreach (var candidateName in new[] { releaseName, tagName }.Where(n => n is not null).Cast<string>())
25
+ {
26
+ foreach (var prefix in prefixesToTrim)
27
+ {
28
+ if (candidateName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
29
+ {
30
+ var trimmedCandidateName = candidateName[prefix.Length..];
31
+ if (NuGetVersion.TryParse(trimmedCandidateName, out var versionFromTrimmed))
32
+ {
33
+ return versionFromTrimmed;
34
+ }
35
+ }
36
+ }
37
+
38
+ // no prefix match, try the whole string
39
+ if (NuGetVersion.TryParse(candidateName, out var versionFromWhole))
40
+ {
41
+ return versionFromWhole;
42
+ }
43
+ }
44
+
45
+ return null;
46
+ }
47
+ }
@@ -0,0 +1,11 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGetUpdater.Core.Run.ApiModel;
4
+ using NuGetUpdater.Core.Updater;
5
+
6
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
7
+
8
+ public interface IPullRequestBodyGenerator
9
+ {
10
+ public Task<string> GeneratePullRequestBodyTextAsync(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, ImmutableArray<ReportedDependency> updatedDependencies);
11
+ }
@@ -0,0 +1,15 @@
1
+ using System.Collections.Immutable;
2
+
3
+ using NuGetUpdater.Core.Run.ApiModel;
4
+ using NuGetUpdater.Core.Updater;
5
+
6
+ namespace NuGetUpdater.Core.Run.PullRequestBodyGenerator;
7
+
8
+ internal class SimplePullRequestBodyGenerator : IPullRequestBodyGenerator
9
+ {
10
+ public Task<string> GeneratePullRequestBodyTextAsync(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, ImmutableArray<ReportedDependency> updatedDependencies)
11
+ {
12
+ var prBody = UpdateOperationBase.GenerateUpdateOperationReport(updateOperationsPerformed, includeFileNames: false);
13
+ return Task.FromResult(prBody);
14
+ }
15
+ }
@@ -3,6 +3,7 @@ using System.Text;
3
3
  using System.Text.RegularExpressions;
4
4
 
5
5
  using NuGetUpdater.Core.Run.ApiModel;
6
+ using NuGetUpdater.Core.Run.PullRequestBodyGenerator;
6
7
  using NuGetUpdater.Core.Updater;
7
8
 
8
9
  using DependencySet = (string Name, (NuGet.Versioning.NuGetVersion? OldVersion, NuGet.Versioning.NuGetVersion NewVersion)[] Versions);
@@ -124,9 +125,12 @@ public class PullRequestTextGenerator
124
125
  return dependencySets;
125
126
  }
126
127
 
127
- public static string GetPullRequestBody(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, string? dependencyGroupName)
128
+ public static async Task<string> GetPullRequestBodyAsync(Job job, ImmutableArray<UpdateOperationBase> updateOperationsPerformed, ImmutableArray<ReportedDependency> updatedDependencies, ExperimentsManager experimentsManager)
128
129
  {
129
- var report = UpdateOperationBase.GenerateUpdateOperationReport(updateOperationsPerformed, includeFileNames: false);
130
- return report;
130
+ var bodyGenerator = experimentsManager.GenerateSimplePrBody
131
+ ? (IPullRequestBodyGenerator)new SimplePullRequestBodyGenerator()
132
+ : new DetailedPullRequestBodyGenerator(new HttpFetcher());
133
+ var prBody = await bodyGenerator.GeneratePullRequestBodyTextAsync(job, updateOperationsPerformed, updatedDependencies);
134
+ return prBody;
131
135
  }
132
136
  }