Skip to content

Commit 8f56ef6

Browse files
bouwkastigoragoli
authored andcommitted
Configure GeneratePackageVersions to support a "cooldown" on dependencies (#8371)
## Summary of changes > default cooldown period is 2 days Adds a configurable cooldown period to the `GeneratePackageVersions` to support remediation efforts for follow up after #incident-51602. To use supply the optional parameter `--PackageVersionCooldownDays X` where `X` is some number of days. The current period is at the moment is going to be 2 days and is the default now when the overall `GeneratePackageVersion` target is ran. Additionally, this is overridden to 0 if `--IncludePackages` is supplied (this is commonly used when working on a singular package locally). After running the tool a "cooldown" report is generated, this file will contain packages that we see have a newer version, but will not incorporate into the test, the fallback version that falls within the cooldown period is provided. This file content will show up in the output of future test package version bump PRs. Note that this is for _automated_ updates, so if it sees something already updated it will honor it. Here's an example output ran locally with 14 days set: ``` ## Package Version Cooldown Report The following versions were published less than **14 days** ago and have been overridden. These require manual review before inclusion. | Package | Integration | Overridden Version | Published | Age (days) | Using Instead | |---------|-------------|--------------------|-----------|------------|---------------| | AWSSDK.Core | AwsSdk | 4.0.3.22 | 2026-03-25 | 0 | 4.0.3.21 | | AWSSDK.S3 | AwsS3 | 4.0.19.2 | 2026-03-25 | 0 | 4.0.19.1 | | StackExchange.Redis | StackExchangeRedis | 2.12.8 | 2026-03-25 | 0 | 2.12.4 | ``` ## Reason for change In #8364 and #incident-51602 all automated dependency updaters to be disabled temporarily, to re-enable we need to supply a 2 day "cooldown" to any version that we update to (in other words the version of the NuGet must be published for at least 2 days before we can update to it). ## Implementation details I made Claude do this 🤖 - NuGetPackageHelper now captures the Published date from IPackageSearchMetadata via a new VersionWithDate record (previously discarded) - NuGetVersionCache stores the new {Version, Published} format - PackageVersionGenerator.ApplyCooldown filters selected versions after LatestMajors/LatestMinors/LatestSpecific selection: - Versions outside the cooldown window pass through unchanged - Versions at or below the baseline (derived from supported_versions.json MaxVersionTestedInclusive) are kept even if within cooldown -- no downgrades - Versions above the baseline and within cooldown are overridden to the best available fallback - CooldownReport collects overridden versions and renders a markdown table saved to tracer/build/cooldown_report.md - The GitHub Actions workflow reads the report and appends it to the auto-bump PR body - Honeypot IntegrationGroups.cs fixes: MSTest.TestFramework now maps to itself, Hangfire.Core maps to Hangfire.Core (was Hangfire), OpenFeature mapping moved to Datadog.FeatureFlags.OpenFeature Passing `--IncludePackages` will override the cooldown to 0 ## Test coverage I ran `GeneratePackageVersions --PackageVersionCooldownDays 14` locally seems good enough IMO (also ran without, with different days etc) ``` [WRN] GeneratePackageVersi: 3 package version(s) were excluded due to the 14-day cooldown period [WRN] GeneratePackageVersi: AWSSDK.Core 4.0.3.22 overridden (published 2026-03-25, using: 4.0.3.21) [WRN] GeneratePackageVersi: AWSSDK.S3 4.0.19.2 overridden (published 2026-03-25, using: 4.0.19.1) [WRN] GeneratePackageVersi: StackExchange.Redis 2.12.8 overridden (published 2026-03-25, using: 2.12.4) ``` ## Other details <!-- Fixes #{issue} --> The workflow file (`auto_bump_test_package_versions.yml`) will be re-enabled with this PR <!-- ⚠️ Note: Where possible, please obtain 2 approvals prior to merging. Unless CODEOWNERS specifies otherwise, for external teams it is typically best to have one review from a team member, and one review from apm-dotnet. Trivial changes do not require 2 reviews. MergeQueue is NOT enabled in this repository. If you have write access to the repo, the PR has 1-2 approvals (see above), and all of the required checks have passed, you can use the Squash and Merge button to merge the PR. If you don't have write access, or you need help, reach out in the #apm-dotnet channel in Slack. -->
1 parent 6221b59 commit 8f56ef6

20 files changed

Lines changed: 57004 additions & 14209 deletions

.github/workflows/auto_bump_test_package_versions.yml.disabled renamed to .github/workflows/auto_bump_test_package_versions.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,21 @@ jobs:
3838
global-json-file: global.json
3939

4040
- name: "Regenerating package versions"
41-
run: .\tracer\build.ps1 GeneratePackageVersions
41+
run: .\tracer\build.ps1 GeneratePackageVersions --PackageVersionCooldownDays 2
42+
43+
- name: Read cooldown report
44+
id: cooldown
45+
if: always()
46+
shell: pwsh
47+
run: |
48+
$report = ""
49+
$reportPath = ".nuke/temp/cooldown_report.md"
50+
if (Test-Path $reportPath) {
51+
$report = Get-Content $reportPath -Raw
52+
}
53+
"report<<EOF" >> $env:GITHUB_OUTPUT
54+
$report >> $env:GITHUB_OUTPUT
55+
"EOF" >> $env:GITHUB_OUTPUT
4256
4357
- name: Create Pull Request
4458
id: pr
@@ -55,6 +69,8 @@ jobs:
5569
body: |
5670
Updates the package versions for integration tests.
5771
72+
${{ steps.cooldown.outputs.report }}
73+
5874
- name: Send Slack notification about generating failure
5975
if: failure()
6076
uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1

tracer/build/PackageVersionsLatestMinors.g.props

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10507,7 +10507,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1050710507
<SampleName>Samples.GoogleProtobuf</SampleName>
1050810508
</PackageVersionSample>
1050910509
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
10510-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
10510+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1051110511
<TargetFramework>net48</TargetFramework>
1051210512
<RequiresDockerDependency>None</RequiresDockerDependency>
1051310513
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -10717,7 +10717,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1071710717
<SampleName>Samples.GoogleProtobuf</SampleName>
1071810718
</PackageVersionSample>
1071910719
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
10720-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
10720+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1072110721
<TargetFramework>netcoreapp2.1</TargetFramework>
1072210722
<RequiresDockerDependency>None</RequiresDockerDependency>
1072310723
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -10927,7 +10927,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1092710927
<SampleName>Samples.GoogleProtobuf</SampleName>
1092810928
</PackageVersionSample>
1092910929
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
10930-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
10930+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1093110931
<TargetFramework>netcoreapp3.0</TargetFramework>
1093210932
<RequiresDockerDependency>None</RequiresDockerDependency>
1093310933
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -11137,7 +11137,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1113711137
<SampleName>Samples.GoogleProtobuf</SampleName>
1113811138
</PackageVersionSample>
1113911139
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
11140-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
11140+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1114111141
<TargetFramework>netcoreapp3.1</TargetFramework>
1114211142
<RequiresDockerDependency>None</RequiresDockerDependency>
1114311143
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -11347,7 +11347,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1134711347
<SampleName>Samples.GoogleProtobuf</SampleName>
1134811348
</PackageVersionSample>
1134911349
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
11350-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
11350+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1135111351
<TargetFramework>net5.0</TargetFramework>
1135211352
<RequiresDockerDependency>None</RequiresDockerDependency>
1135311353
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -11557,7 +11557,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1155711557
<SampleName>Samples.GoogleProtobuf</SampleName>
1155811558
</PackageVersionSample>
1155911559
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
11560-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
11560+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1156111561
<TargetFramework>net6.0</TargetFramework>
1156211562
<RequiresDockerDependency>None</RequiresDockerDependency>
1156311563
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -11767,7 +11767,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1176711767
<SampleName>Samples.GoogleProtobuf</SampleName>
1176811768
</PackageVersionSample>
1176911769
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
11770-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
11770+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1177111771
<TargetFramework>net7.0</TargetFramework>
1177211772
<RequiresDockerDependency>None</RequiresDockerDependency>
1177311773
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -11977,7 +11977,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1197711977
<SampleName>Samples.GoogleProtobuf</SampleName>
1197811978
</PackageVersionSample>
1197911979
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
11980-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
11980+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1198111981
<TargetFramework>net8.0</TargetFramework>
1198211982
<RequiresDockerDependency>None</RequiresDockerDependency>
1198311983
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -12187,7 +12187,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1218712187
<SampleName>Samples.GoogleProtobuf</SampleName>
1218812188
</PackageVersionSample>
1218912189
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
12190-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
12190+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1219112191
<TargetFramework>net9.0</TargetFramework>
1219212192
<RequiresDockerDependency>None</RequiresDockerDependency>
1219312193
<SampleName>Samples.GoogleProtobuf</SampleName>
@@ -12397,7 +12397,7 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
1239712397
<SampleName>Samples.GoogleProtobuf</SampleName>
1239812398
</PackageVersionSample>
1239912399
<PackageVersionSample Include="test\test-applications\integrations\Samples.GoogleProtobuf\Samples.GoogleProtobuf.csproj">
12400-
<Properties>ApiVersion=3.25.8;RestoreRecursive=false;BuildProjectReferences=false</Properties>
12400+
<Properties>ApiVersion=3.25.9;RestoreRecursive=false;BuildProjectReferences=false</Properties>
1240112401
<TargetFramework>net10.0</TargetFramework>
1240212402
<RequiresDockerDependency>None</RequiresDockerDependency>
1240312403
<SampleName>Samples.GoogleProtobuf</SampleName>

tracer/build/PackageVersionsLatestSpecific.g.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7051,13 +7051,13 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
70517051
<SampleName>Samples.CosmosDb.Vnext</SampleName>
70527052
</PackageVersionSample>
70537053
<PackageVersionSample Include="test\test-applications\integrations\LogsInjection.Serilog\LogsInjection.Serilog.csproj">
7054-
<Properties>ApiVersion=1.5.14;RestoreRecursive=false;BuildProjectReferences=false</Properties>
7054+
<Properties>ApiVersion=1.4.214;RestoreRecursive=false;BuildProjectReferences=false</Properties>
70557055
<TargetFramework>net48</TargetFramework>
70567056
<RequiresDockerDependency>None</RequiresDockerDependency>
70577057
<SampleName>LogsInjection.Serilog</SampleName>
70587058
</PackageVersionSample>
70597059
<PackageVersionSample Include="test\test-applications\integrations\LogsInjection.Serilog\LogsInjection.Serilog.csproj">
7060-
<Properties>ApiVersion=1.4.214;RestoreRecursive=false;BuildProjectReferences=false</Properties>
7060+
<Properties>ApiVersion=1.5.14;RestoreRecursive=false;BuildProjectReferences=false</Properties>
70617061
<TargetFramework>net48</TargetFramework>
70627062
<RequiresDockerDependency>None</RequiresDockerDependency>
70637063
<SampleName>LogsInjection.Serilog</SampleName>

tracer/build/_build/Build.Utilities.cs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ partial class Build
5959
[Parameter("Only update package versions for packages with the following names")]
6060
readonly string[] IncludePackages;
6161

62+
[Parameter("Minimum age in days a NuGet package version must have been published before auto-including. Defaults to 2 days, or 0 when --IncludePackages is set")]
63+
readonly int? PackageVersionCooldownDays;
64+
6265
[LazyLocalExecutable(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\gacutil.exe")]
6366
readonly Lazy<Tool> GacUtil;
6467
[LazyLocalExecutable(@"C:\Program Files\IIS Express\iisexpress.exe")]
@@ -247,11 +250,95 @@ partial class Build
247250
var previousSupportedVersions = await GenerateSupportMatrix.LoadPreviousVersions(supportedVersionsPath);
248251
Logger.Information("Loaded previous supported versions with {Count} entries", previousSupportedVersions.Count);
249252

253+
// Derive baseline from supported_versions.json: the max tested version per package
254+
// acts as a floor to prevent cooldown filtering from downgrading previously accepted versions.
255+
// We collect all max tested versions per package (not just the global max) so that
256+
// split-range packages (e.g., GraphQL 4.x-6.x and 7.x-9.x) get a per-range baseline.
257+
var baseline = previousSupportedVersions
258+
.Where(kvp => kvp.Value.MaxVersionTestedInclusive is not null)
259+
.GroupBy(kvp => kvp.Key.PackageName)
260+
.ToDictionary(
261+
g => g.Key,
262+
g => g.Select(kvp => new Version(kvp.Value.MaxVersionTestedInclusive!)).ToList());
263+
Logger.Information("Derived version baseline with {Count} entries from supported_versions.json", baseline.Count);
264+
265+
// Resolve effective cooldown:
266+
// - Explicit --PackageVersionCooldownDays wins
267+
// - --IncludePackages without explicit cooldown defaults to 0
268+
var effectiveCooldownDays = PackageVersionCooldownDays ?? (IncludePackages is not null ? 0 : 2);
269+
250270
// Pipeline A: generate .g.props/.g.cs files
251-
var versionGenerator = new PackageVersionGenerator(TracerDirectory, testDir, shouldUpdatePackage, previousVersionCache);
271+
Logger.Information("Using package version cooldown of {Days} days", effectiveCooldownDays);
272+
var versionGenerator = new PackageVersionGenerator(TracerDirectory, testDir, shouldUpdatePackage, previousVersionCache, effectiveCooldownDays, baseline);
252273
var testedVersions = await versionGenerator.GenerateVersions(Solution);
253274
await NuGetVersionCache.Save(cacheFilePath, versionGenerator.VersionCache);
254275

276+
// Log version changes: bumps, unchanged, and overridden
277+
var versionCache = versionGenerator.VersionCache;
278+
var bumped = 0;
279+
var unchanged = 0;
280+
foreach (var tested in testedVersions)
281+
{
282+
var packageName = tested.NugetPackageSearchName;
283+
baseline.TryGetValue(packageName, out var previousMaxVersions);
284+
var previousMax = previousMaxVersions?
285+
.Where(v => v >= tested.MinVersion && v <= tested.MaxVersion)
286+
.OrderByDescending(v => v)
287+
.FirstOrDefault();
288+
289+
if (previousMax is null || tested.MaxVersion > previousMax)
290+
{
291+
bumped++;
292+
var publishedDate = "(unknown)";
293+
if (versionCache.TryGetValue(packageName, out var cachedVersions))
294+
{
295+
var match = cachedVersions.FirstOrDefault(v => v.Version == tested.MaxVersion.ToString());
296+
if (match?.Published is not null)
297+
{
298+
publishedDate = match.Published.Value.ToString("yyyy-MM-dd");
299+
}
300+
}
301+
302+
Logger.Information(
303+
" {Package} {Previous} -> {Current} (published {Date}, https://www.nuget.org/packages/{Package}/{Current})",
304+
packageName,
305+
previousMax?.ToString() ?? "(new)",
306+
tested.MaxVersion,
307+
publishedDate,
308+
packageName,
309+
tested.MaxVersion);
310+
}
311+
else
312+
{
313+
unchanged++;
314+
}
315+
}
316+
317+
Logger.Information("{Bumped} package(s) bumped, {Unchanged} unchanged", bumped, unchanged);
318+
319+
if (versionGenerator.CooldownReport.HasEntries)
320+
{
321+
Logger.Warning(
322+
"{Count} package version(s) were excluded due to the {Days}-day cooldown period",
323+
versionGenerator.CooldownReport.Entries.Count,
324+
effectiveCooldownDays);
325+
326+
foreach (var entry in versionGenerator.CooldownReport.Entries)
327+
{
328+
var resolvedText = entry.ResolvedVersion is not null ? $"using: {entry.ResolvedVersion}" : "skipped";
329+
Logger.Warning(
330+
" {Package} {Version} overridden (published {Date}, {Resolved})",
331+
entry.PackageName,
332+
entry.OverriddenVersion,
333+
entry.PublishedDate?.ToString("yyyy-MM-dd") ?? "unknown",
334+
resolvedText);
335+
}
336+
337+
var reportPath = TemporaryDirectory / "cooldown_report.md";
338+
await versionGenerator.CooldownReport.SaveToFile(reportPath);
339+
Logger.Information("Cooldown report saved to {Path}", reportPath);
340+
}
341+
255342
var assemblies = MonitoringHomeDirectory
256343
.GlobFiles("**/Datadog.Trace.dll")
257344
.Select(x => x.ToString())
@@ -260,6 +347,8 @@ partial class Build
260347
var integrations = GenerateIntegrationDefinitions.GetAllIntegrations(assemblies, definitionsFile);
261348

262349
// Pipeline B: generate dependabot files + supported_versions.json
350+
// TestedVersions are cooldown-filtered but the baseline prevents downgrades,
351+
// so they accurately reflect what we're testing.
263352
var distinctIntegrations = await DependabotFileManager.BuildDistinctIntegrationMaps(
264353
integrations, testedVersions, shouldUpdatePackage, previousSupportedVersions);
265354

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// <copyright file="CooldownReport.cs" company="Datadog">
2+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
3+
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
4+
// </copyright>
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Text;
10+
using System.Threading.Tasks;
11+
12+
namespace GeneratePackageVersions;
13+
14+
/// <summary>
15+
/// Collects package versions that were excluded by the cooldown filter
16+
/// and renders them as a markdown report for inclusion in the PR body.
17+
/// </summary>
18+
public class CooldownReport
19+
{
20+
private readonly int _cooldownDays;
21+
private readonly List<CooldownEntry> _entries = new();
22+
private readonly HashSet<(string PackageName, string Version)> _seen = new();
23+
24+
public CooldownReport(int cooldownDays)
25+
{
26+
_cooldownDays = cooldownDays;
27+
}
28+
29+
public bool HasEntries => _entries.Count > 0;
30+
31+
public IReadOnlyList<CooldownEntry> Entries => _entries;
32+
33+
public void Add(CooldownEntry entry)
34+
{
35+
// Deduplicate: the same version gets flagged once per framework and per selection group,
36+
// but we only need to report it once.
37+
if (_seen.Add((entry.PackageName, entry.OverriddenVersion)))
38+
{
39+
_entries.Add(entry);
40+
}
41+
}
42+
43+
public string ToMarkdown()
44+
{
45+
if (_entries.Count == 0)
46+
{
47+
return string.Empty;
48+
}
49+
50+
var sb = new StringBuilder();
51+
sb.AppendLine($"## Package Version Cooldown Report");
52+
sb.AppendLine();
53+
sb.AppendLine($"The following versions were published less than **{_cooldownDays} days** ago and have been overridden.");
54+
sb.AppendLine("These require manual review before inclusion.");
55+
sb.AppendLine();
56+
sb.AppendLine("| Package | Integration | Overridden Version | Published | Age (days) | Using Instead |");
57+
sb.AppendLine("|---------|-------------|--------------------|-----------|------------|---------------|");
58+
59+
foreach (var entry in _entries)
60+
{
61+
var published = entry.PublishedDate?.ToString("yyyy-MM-dd") ?? "unknown";
62+
var age = entry.PublishedDate.HasValue
63+
? ((int)(DateTimeOffset.UtcNow - entry.PublishedDate.Value).TotalDays).ToString()
64+
: "?";
65+
var usingInstead = entry.ResolvedVersion ?? "(skipped)";
66+
67+
sb.AppendLine($"| {entry.PackageName} | {entry.IntegrationName} | {entry.OverriddenVersion} | {published} | {age} | {usingInstead} |");
68+
}
69+
70+
return sb.ToString();
71+
}
72+
73+
public async Task SaveToFile(string path)
74+
{
75+
var markdown = ToMarkdown();
76+
if (!string.IsNullOrEmpty(markdown))
77+
{
78+
// necessary to save to a file so that we can then output it in the PR description
79+
await File.WriteAllTextAsync(path, markdown);
80+
}
81+
}
82+
83+
public record CooldownEntry(
84+
string PackageName,
85+
string IntegrationName,
86+
string OverriddenVersion,
87+
DateTimeOffset? PublishedDate,
88+
string ResolvedVersion);
89+
}

0 commit comments

Comments
 (0)