Skip to content

Commit 1b2d75d

Browse files
committed
Update changelog render date handling (#3260)
1 parent 29af28e commit 1b2d75d

3 files changed

Lines changed: 216 additions & 4 deletions

File tree

docs/cli/changelog/render.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ The `render` command automatically discovers and merges `.amend-*.yaml` files wi
6464

6565
`--title <string?>`
6666
: Optional: The title to use for section headers, directories, and anchors in output files.
67-
: Defaults to the version in the first bundle.
67+
: Defaults to the version in the first bundle. When omitted, ISO date targets are formatted for display the same way as the `{changelog}` directive (e.g., `2026-05-04` becomes "May 4, 2026", `2026-05` becomes "May 2026"), while directory names and heading anchors continue to use the raw target slug.
6868
: If the string contains spaces, they are replaced with dashes when used in directory names and anchors.
6969

7070
The `changelog render` command does **not** use `rules.publish` for filtering. Filtering must be done at bundle time using `rules.bundle`. For more information, refer to [](/contribute/publish-changelogs.md). For how the directive differs, see the [{changelog} directive syntax reference](/syntax/changelog.md).

src/services/Elastic.Changelog/Rendering/ChangelogRenderingService.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,23 @@ private OutputSetup SetupOutput(
227227
if (string.IsNullOrWhiteSpace(input.Title) && version == "unknown")
228228
collector.EmitWarning(string.Empty, "No --title option provided and bundle files do not contain 'target' values. Output folder and markdown titles will default to 'unknown'. Consider using --title to specify a custom title.");
229229

230-
// Use title from input or default to version
231-
var title = input.Title ?? version;
232-
var titleSlug = ChangelogTextUtilities.TitleToSlug(title);
230+
// Determine title and slug
231+
string title;
232+
string titleSlug;
233+
234+
if (string.IsNullOrWhiteSpace(input.Title))
235+
{
236+
// Default title: format dates like the changelog directive
237+
title = VersionOrDate.FormatDisplayVersion(version);
238+
// Slug always uses raw version to maintain consistent paths/anchors
239+
titleSlug = ChangelogTextUtilities.TitleToSlug(version);
240+
}
241+
else
242+
{
243+
// Explicit title provided: use as-is for both title and slug
244+
title = input.Title;
245+
titleSlug = ChangelogTextUtilities.TitleToSlug(input.Title);
246+
}
233247

234248
return new OutputSetup(outputDir, title, titleSlug);
235249
}

tests/Elastic.Changelog.Tests/Changelogs/Render/TitleTargetTests.cs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,202 @@ public async Task RenderChangelogs_WithTitleAndNoTargets_NoWarning()
132132
d.Severity == Severity.Warning &&
133133
d.Message.Contains("No --title option provided"));
134134
}
135+
136+
[Fact]
137+
public async Task RenderChangelogs_WithIsoDateTarget_FormatsDateInHeading()
138+
{
139+
// Arrange
140+
var changelogDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
141+
FileSystem.Directory.CreateDirectory(changelogDir);
142+
143+
// Create test changelog file with ISO date target
144+
// language=yaml
145+
var changelog1 =
146+
"""
147+
title: Test feature
148+
type: feature
149+
products:
150+
- product: elasticsearch
151+
target: 2026-05-04
152+
prs:
153+
- "100"
154+
""";
155+
156+
var changelogFile = FileSystem.Path.Join(changelogDir, "1755268130-test-feature.yaml");
157+
await FileSystem.File.WriteAllTextAsync(changelogFile, changelog1, TestContext.Current.CancellationToken);
158+
159+
// Create bundle file with ISO date target
160+
var bundleDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
161+
FileSystem.Directory.CreateDirectory(bundleDir);
162+
163+
var bundleFile = FileSystem.Path.Join(bundleDir, "bundle.yaml");
164+
// language=yaml
165+
var bundleContent =
166+
$"""
167+
products:
168+
- product: elasticsearch
169+
target: 2026-05-04
170+
entries:
171+
- file:
172+
name: 1755268130-test-feature.yaml
173+
checksum: {ComputeSha1(changelog1)}
174+
""";
175+
await FileSystem.File.WriteAllTextAsync(bundleFile, bundleContent, TestContext.Current.CancellationToken);
176+
177+
var outputDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
178+
179+
var input = new RenderChangelogsArguments
180+
{
181+
Bundles = [new BundleInput { BundleFile = bundleFile, Directory = changelogDir }],
182+
Output = outputDir
183+
// Note: Title is not set, should default to formatted date
184+
};
185+
186+
// Act
187+
var result = await Service.RenderChangelogs(Collector, input, TestContext.Current.CancellationToken);
188+
189+
// Assert
190+
result.Should().BeTrue();
191+
Collector.Errors.Should().Be(0);
192+
193+
// Check that output directory uses raw date slug
194+
var indexFile = FileSystem.Path.Join(outputDir, "2026-05-04", "index.md");
195+
FileSystem.File.Exists(indexFile).Should().BeTrue();
196+
197+
// Check that heading uses formatted date but anchor uses raw date
198+
var indexContent = await FileSystem.File.ReadAllTextAsync(indexFile, TestContext.Current.CancellationToken);
199+
indexContent.Should().Contain("## May 4, 2026 [elastic-release-notes-2026-05-04]");
200+
}
201+
202+
[Fact]
203+
public async Task RenderChangelogs_WithYearMonthTarget_FormatsDateInHeading()
204+
{
205+
// Arrange
206+
var changelogDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
207+
FileSystem.Directory.CreateDirectory(changelogDir);
208+
209+
// Create test changelog file with year-month target
210+
// language=yaml
211+
var changelog1 =
212+
"""
213+
title: Test feature
214+
type: feature
215+
products:
216+
- product: elasticsearch
217+
target: 2026-05
218+
prs:
219+
- "100"
220+
""";
221+
222+
var changelogFile = FileSystem.Path.Join(changelogDir, "1755268130-test-feature.yaml");
223+
await FileSystem.File.WriteAllTextAsync(changelogFile, changelog1, TestContext.Current.CancellationToken);
224+
225+
// Create bundle file with year-month target
226+
var bundleDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
227+
FileSystem.Directory.CreateDirectory(bundleDir);
228+
229+
var bundleFile = FileSystem.Path.Join(bundleDir, "bundle.yaml");
230+
// language=yaml
231+
var bundleContent =
232+
$"""
233+
products:
234+
- product: elasticsearch
235+
target: 2026-05
236+
entries:
237+
- file:
238+
name: 1755268130-test-feature.yaml
239+
checksum: {ComputeSha1(changelog1)}
240+
""";
241+
await FileSystem.File.WriteAllTextAsync(bundleFile, bundleContent, TestContext.Current.CancellationToken);
242+
243+
var outputDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
244+
245+
var input = new RenderChangelogsArguments
246+
{
247+
Bundles = [new BundleInput { BundleFile = bundleFile, Directory = changelogDir }],
248+
Output = outputDir
249+
// Note: Title is not set, should default to formatted date
250+
};
251+
252+
// Act
253+
var result = await Service.RenderChangelogs(Collector, input, TestContext.Current.CancellationToken);
254+
255+
// Assert
256+
result.Should().BeTrue();
257+
Collector.Errors.Should().Be(0);
258+
259+
// Check that output directory uses raw date slug
260+
var indexFile = FileSystem.Path.Join(outputDir, "2026-05", "index.md");
261+
FileSystem.File.Exists(indexFile).Should().BeTrue();
262+
263+
// Check that heading uses formatted date but anchor uses raw date
264+
var indexContent = await FileSystem.File.ReadAllTextAsync(indexFile, TestContext.Current.CancellationToken);
265+
indexContent.Should().Contain("## May 2026 [elastic-release-notes-2026-05]");
266+
}
267+
268+
[Fact]
269+
public async Task RenderChangelogs_WithExplicitDateTitle_DoesNotFormatTitle()
270+
{
271+
// Arrange
272+
var changelogDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
273+
FileSystem.Directory.CreateDirectory(changelogDir);
274+
275+
// Create test changelog file with ISO date target
276+
// language=yaml
277+
var changelog1 =
278+
"""
279+
title: Test feature
280+
type: feature
281+
products:
282+
- product: elasticsearch
283+
target: 2026-05-04
284+
prs:
285+
- "100"
286+
""";
287+
288+
var changelogFile = FileSystem.Path.Join(changelogDir, "1755268130-test-feature.yaml");
289+
await FileSystem.File.WriteAllTextAsync(changelogFile, changelog1, TestContext.Current.CancellationToken);
290+
291+
// Create bundle file with ISO date target
292+
var bundleDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
293+
FileSystem.Directory.CreateDirectory(bundleDir);
294+
295+
var bundleFile = FileSystem.Path.Join(bundleDir, "bundle.yaml");
296+
// language=yaml
297+
var bundleContent =
298+
$"""
299+
products:
300+
- product: elasticsearch
301+
target: 2026-05-04
302+
entries:
303+
- file:
304+
name: 1755268130-test-feature.yaml
305+
checksum: {ComputeSha1(changelog1)}
306+
""";
307+
await FileSystem.File.WriteAllTextAsync(bundleFile, bundleContent, TestContext.Current.CancellationToken);
308+
309+
var outputDir = FileSystem.Path.Join(Paths.WorkingDirectoryRoot.FullName, Guid.NewGuid().ToString());
310+
311+
var input = new RenderChangelogsArguments
312+
{
313+
Bundles = [new BundleInput { BundleFile = bundleFile, Directory = changelogDir }],
314+
Output = outputDir,
315+
Title = "2026-05-04" // Explicit title provided - should stay literal
316+
};
317+
318+
// Act
319+
var result = await Service.RenderChangelogs(Collector, input, TestContext.Current.CancellationToken);
320+
321+
// Assert
322+
result.Should().BeTrue();
323+
Collector.Errors.Should().Be(0);
324+
325+
// Check that output directory uses title slug
326+
var indexFile = FileSystem.Path.Join(outputDir, "2026-05-04", "index.md");
327+
FileSystem.File.Exists(indexFile).Should().BeTrue();
328+
329+
// Check that heading uses literal title (no formatting applied)
330+
var indexContent = await FileSystem.File.ReadAllTextAsync(indexFile, TestContext.Current.CancellationToken);
331+
indexContent.Should().Contain("## 2026-05-04 [elastic-release-notes-2026-05-04]");
332+
}
135333
}

0 commit comments

Comments
 (0)