Skip to content

Commit 29af28e

Browse files
committed
Add changelog render --dropdowns
1 parent 89c8790 commit 29af28e

9 files changed

Lines changed: 781 additions & 47 deletions

File tree

docs/cli/changelog/render.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# changelog render
2+
3+
Generate markdown or asciidoc files from changelog bundle files.
4+
5+
To create the bundle files, use [](/cli/changelog/bundle.md).
6+
For details and examples, go to [](/contribute/publish-changelogs.md).
7+
8+
## Usage
9+
10+
```sh
11+
docs-builder changelog render [options...] [-h|--help]
12+
```
13+
14+
## Options
15+
16+
`--config <string?>`
17+
: Optional: Path to the changelog.yml configuration file.
18+
: Defaults to `docs/changelog.yml`.
19+
: Note: The `changelog render` command does not use `rules.publish` for filtering. Filtering must be done at bundle time using `rules.bundle`.
20+
21+
`--hide-features <string[]?>`
22+
: Optional: Filter by feature IDs (comma-separated), or a path to a newline-delimited file containing feature IDs. Can be specified multiple times.
23+
: Each occurrence can be either comma-separated feature IDs (e.g., `--hide-features "feature:new-search-api,feature:enhanced-analytics"`) or a file path (e.g., `--hide-features /path/to/file.txt`).
24+
: When specifying feature IDs directly, provide comma-separated values.
25+
: When specifying a file path, provide a single value that points to a newline-delimited file. The file should contain one feature ID per line.
26+
: Entries with matching `feature-id` values will be commented out in the output and a warning will be emitted.
27+
: If the bundle contains `hide-features` values (that is to say, it was created with the `--hide-features` option), those values are merged with this list and are also hidden.
28+
29+
`--input <string[]>`
30+
: One or more bundle input files.
31+
: Each bundle is specified as "bundle-file-path|changelog-file-path|repo|link-visibility" using pipe (`|`) as delimiter.
32+
: To merge multiple bundles, separate them with commas: `--input "bundle1|dir1|repo1|keep-links,bundle2|dir2|repo2|hide-links"`.
33+
: For example, `--input "/path/to/changelog-bundle.yaml|/path/to/changelogs|elasticsearch|keep-links"`.
34+
: Only `bundle-file-path` is required for each bundle.
35+
: Use `repo` if your changelogs do not contain full URLs for the pull requests or issues; otherwise they will be incorrectly derived with "elastic/elastic" in the URL by default.
36+
: Use `link-visibility` to control whether PR/issue links are shown or hidden for entries from this bundle. Valid values are `keep-links` (default) or `hide-links`. Use `hide-links` for bundles from private repositories. When `hide-links` is set, all links are hidden for each affected entry — changelog entries can contain multiple PR links (`prs`) and issue links (`issues`), and all of them are hidden or shown together.
37+
: Paths support tilde (`~`) expansion and relative paths.
38+
39+
:::{note}
40+
The `render` command automatically discovers and merges `.amend-*.yaml` files with their parent bundle. For more information about amended bundles, go to [](bundle-amend.md).
41+
:::
42+
43+
`--file-type <string>`
44+
: Optional: Output file type. Valid values: `"markdown"` or `"asciidoc"`.
45+
: Defaults to `"markdown"`.
46+
: When `"markdown"` is specified, the command generates multiple markdown files (index.md, breaking-changes.md, deprecations.md, known-issues.md).
47+
: When `"asciidoc"` is specified, the command generates a single asciidoc file with all sections.
48+
49+
`--output <string?>`
50+
: Optional: The output directory for rendered files.
51+
: Defaults to current directory.
52+
53+
`--subsections`
54+
: Optional: Group entries by area in subsections.
55+
: Defaults to false.
56+
: When enabled, entries are grouped by their area within each section. The first area from each entry's areas list is used for grouping.
57+
58+
`--dropdowns`
59+
: Optional: Render separated types (breaking changes, deprecations, known issues, highlights) as MyST dropdowns.
60+
: Defaults to false (flattened bulleted lists).
61+
: When enabled, each entry in separated files is rendered as a collapsible dropdown section using MyST syntax (`::::{dropdown}`).
62+
: When disabled (default), entries are rendered as flattened bulleted lists with PR/issue links inline and Impact/Action sections indented.
63+
: This flag only affects markdown output; AsciiDoc output always uses its standard format.
64+
65+
`--title <string?>`
66+
: Optional: The title to use for section headers, directories, and anchors in output files.
67+
: Defaults to the version in the first bundle.
68+
: If the string contains spaces, they are replaced with dashes when used in directory names and anchors.
69+
70+
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).
71+
72+
## Output formats
73+
74+
### Markdown format
75+
76+
When `--file-type markdown` is specified (the default), the command generates multiple markdown files:
77+
78+
- `index.md` - Contains features, enhancements, bug fixes, security updates, documentation changes, regressions, and other changes
79+
- `breaking-changes.md` - Contains breaking changes
80+
- `deprecations.md` - Contains deprecations
81+
- `known-issues.md` - Contains known issues
82+
- `highlights.md` - Contains highlighted entries (only created when at least one entry has `highlight: true`)
83+
84+
### Asciidoc format
85+
86+
When `--file-type asciidoc` is specified, the command generates a single asciidoc file with all sections:
87+
88+
- Security updates
89+
- Bug fixes
90+
- Highlights (only included when at least one entry has `highlight: true`)
91+
- New features and enhancements
92+
- Breaking changes
93+
- Deprecations
94+
- Known issues
95+
- Documentation
96+
- Regressions
97+
- Other changes
98+
99+
The asciidoc output uses attribute references for links (for example, `{repo-pull}NUMBER[#NUMBER]`).
100+
101+
### Multiple PR and issue links
102+
103+
Changelog entries can reference multiple pull requests and issues using the `prs` and `issues` array fields. When an entry has multiple links, all of them are rendered inline for that entry:
104+
105+
```md
106+
* Fix ML calendar event update scalability issues. [#136886](https://github.com/elastic/elastic/pull/136886) [#136900](https://github.com/elastic/elastic/pull/136900)
107+
```
108+
109+
## Examples
110+
111+
### Render a single bundle
112+
113+
```sh
114+
docs-builder changelog render \
115+
--input "./docs/changelog/bundles/9.3.0.yaml" \
116+
--output ./release-notes
117+
```
118+
119+
### Render with tilde expansion
120+
121+
```sh
122+
docs-builder changelog render \
123+
--input "~/docs/changelog/bundles/9.3.0.yaml|~/docs/changelog|elasticsearch" \
124+
--output ~/release-notes
125+
```
126+
127+
### Render with relative paths
128+
129+
```sh
130+
docs-builder changelog render \
131+
--input "./bundles/9.3.0.yaml|./changelog|elasticsearch|keep-links" \
132+
--file-type markdown \
133+
--output ./output
134+
```
135+
136+
### Merge multiple bundles
137+
138+
```sh
139+
docs-builder changelog render \
140+
--input "./bundles/elasticsearch-9.3.0.yaml|./changelog|elasticsearch,./bundles/kibana-9.3.0.yaml|./changelog|kibana" \
141+
--output ./merged-release-notes
142+
```
143+
144+
### Hide links from private repository bundles
145+
146+
```sh
147+
docs-builder changelog render \
148+
--input "./public-bundle.yaml|./changelog|elasticsearch|keep-links,./private-bundle.yaml|./private-changelog|internal-repo|hide-links" \
149+
--output ./release-notes
150+
```
151+
152+
### Render with dropdown format
153+
154+
```sh
155+
docs-builder changelog render \
156+
--input "./bundles/9.3.0.yaml|./changelog|elasticsearch" \
157+
--dropdowns \
158+
--output ./release-notes
159+
```
160+
161+
### Render with subsections and flattened format (default)
162+
163+
```sh
164+
docs-builder changelog render \
165+
--input "./bundles/9.3.0.yaml|./changelog|elasticsearch" \
166+
--subsections \
167+
--output ./release-notes
168+
```

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public record ChangelogRenderContext
2020
public required string Owner { get; init; }
2121
public required IReadOnlyDictionary<ChangelogEntryType, IReadOnlyCollection<ChangelogEntry>> EntriesByType { get; init; }
2222
public required bool Subsections { get; init; }
23+
public required bool Dropdowns { get; init; }
2324
public required HashSet<string> FeatureIdsToHide { get; init; }
2425
public required Dictionary<ChangelogEntry, HashSet<string>> EntryToBundleProducts { get; init; }
2526
public required Dictionary<ChangelogEntry, string> EntryToRepo { get; init; }

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public record RenderChangelogsArguments
2828
public string? Output { get; init; }
2929
public string? Title { get; init; }
3030
public bool Subsections { get; init; }
31+
public bool Dropdowns { get; init; }
3132
public string[]? HideFeatures { get; init; }
3233
public string? Config { get; init; }
3334
public ChangelogFileType FileType { get; init; } = ChangelogFileType.Markdown;
@@ -325,6 +326,7 @@ private static ChangelogRenderContext BuildRenderContext(
325326
Owner = ownerForAnchors,
326327
EntriesByType = entriesByType,
327328
Subsections = input.Subsections,
329+
Dropdowns = input.Dropdowns,
328330
FeatureIdsToHide = featureIdsToHide,
329331
EntryToBundleProducts = entryToBundleProducts,
330332
EntryToRepo = entryToRepo,

src/services/Elastic.Changelog/Rendering/Markdown/BreakingChangesMarkdownRenderer.cs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,58 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct
6363
_ = sb.AppendLine();
6464
if (shouldHide)
6565
_ = sb.AppendLine("<!--");
66-
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
67-
_ = sb.AppendLine(entry.Description ?? "% Describe the functionality that changed");
68-
_ = sb.AppendLine();
69-
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
70-
71-
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Impact)
72-
? "**Impact**<br>" + entry.Impact
73-
: "% **Impact**<br>_Add a description of the impact_");
74-
75-
_ = sb.AppendLine();
7666

77-
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Action)
78-
? "**Action**<br>" + entry.Action
79-
: "% **Action**<br>_Add a description of the what action to take_");
67+
if (context.Dropdowns)
68+
{
69+
// Dropdown rendering (current logic)
70+
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
71+
_ = sb.AppendLine(entry.Description ?? "% Describe the functionality that changed");
72+
_ = sb.AppendLine();
73+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
74+
75+
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Impact)
76+
? "**Impact**<br>" + entry.Impact
77+
: "% **Impact**<br>_Add a description of the impact_");
78+
79+
_ = sb.AppendLine();
80+
81+
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Action)
82+
? "**Action**<br>" + entry.Action
83+
: "% **Action**<br>_Add a description of the what action to take_");
84+
85+
_ = sb.AppendLine("::::");
86+
}
87+
else
88+
{
89+
// Flattened rendering
90+
_ = sb.Append("* ");
91+
_ = sb.Append(ChangelogTextUtilities.Beautify(entry.Title));
92+
_ = sb.AppendLine();
93+
94+
// Description with proper indentation
95+
if (!string.IsNullOrWhiteSpace(entry.Description))
96+
{
97+
_ = sb.AppendLine(ChangelogTextUtilities.Indent(entry.Description));
98+
_ = sb.AppendLine();
99+
}
100+
101+
// PR/Issue links with "For more information" pattern
102+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
103+
104+
// Impact and Action sections
105+
if (!string.IsNullOrWhiteSpace(entry.Impact))
106+
{
107+
_ = sb.AppendLine("**Impact:** " + entry.Impact);
108+
_ = sb.AppendLine();
109+
}
110+
111+
if (!string.IsNullOrWhiteSpace(entry.Action))
112+
{
113+
_ = sb.AppendLine("**Action:** " + entry.Action);
114+
_ = sb.AppendLine();
115+
}
116+
}
80117

81-
_ = sb.AppendLine("::::");
82118
if (shouldHide)
83119
_ = sb.AppendLine("-->");
84120
}

src/services/Elastic.Changelog/Rendering/Markdown/DeprecationsMarkdownRenderer.cs

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,58 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct
6060
_ = sb.AppendLine();
6161
if (shouldHide)
6262
_ = sb.AppendLine("<!--");
63-
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
64-
_ = sb.AppendLine(entry.Description ?? "% Describe the functionality that was deprecated");
65-
_ = sb.AppendLine();
66-
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
67-
68-
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Impact)
69-
? "**Impact**<br>" + entry.Impact
70-
: "% **Impact**<br>_Add a description of the impact_");
71-
72-
_ = sb.AppendLine();
7363

74-
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Action)
75-
? "**Action**<br>" + entry.Action
76-
: "% **Action**<br>_Add a description of the what action to take_");
64+
if (context.Dropdowns)
65+
{
66+
// Dropdown rendering (current logic)
67+
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
68+
_ = sb.AppendLine(entry.Description ?? "% Describe the functionality that was deprecated");
69+
_ = sb.AppendLine();
70+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
71+
72+
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Impact)
73+
? "**Impact**<br>" + entry.Impact
74+
: "% **Impact**<br>_Add a description of the impact_");
75+
76+
_ = sb.AppendLine();
77+
78+
_ = sb.AppendLine(!string.IsNullOrWhiteSpace(entry.Action)
79+
? "**Action**<br>" + entry.Action
80+
: "% **Action**<br>_Add a description of the what action to take_");
81+
82+
_ = sb.AppendLine("::::");
83+
}
84+
else
85+
{
86+
// Flattened rendering
87+
_ = sb.Append("* ");
88+
_ = sb.Append(ChangelogTextUtilities.Beautify(entry.Title));
89+
_ = sb.AppendLine();
90+
91+
// Description with proper indentation
92+
if (!string.IsNullOrWhiteSpace(entry.Description))
93+
{
94+
_ = sb.AppendLine(ChangelogTextUtilities.Indent(entry.Description));
95+
_ = sb.AppendLine();
96+
}
97+
98+
// PR/Issue links with "For more information" pattern
99+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
100+
101+
// Impact and Action sections
102+
if (!string.IsNullOrWhiteSpace(entry.Impact))
103+
{
104+
_ = sb.AppendLine("**Impact:** " + entry.Impact);
105+
_ = sb.AppendLine();
106+
}
107+
108+
if (!string.IsNullOrWhiteSpace(entry.Action))
109+
{
110+
_ = sb.AppendLine("**Action:** " + entry.Action);
111+
_ = sb.AppendLine();
112+
}
113+
}
77114

78-
_ = sb.AppendLine("::::");
79115
if (shouldHide)
80116
_ = sb.AppendLine("-->");
81117
}

src/services/Elastic.Changelog/Rendering/Markdown/HighlightsMarkdownRenderer.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,34 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct
6363
_ = sb.AppendLine();
6464
if (shouldHide)
6565
_ = sb.AppendLine("<!--");
66-
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
67-
_ = sb.AppendLine(entry.Description ?? "% Describe the highlight");
68-
_ = sb.AppendLine();
69-
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
70-
_ = sb.AppendLine("::::");
66+
67+
if (context.Dropdowns)
68+
{
69+
// Dropdown rendering (current logic)
70+
_ = sb.AppendLine(InvariantCulture, $"::::{{dropdown}} {ChangelogTextUtilities.Beautify(entry.Title)}");
71+
_ = sb.AppendLine(entry.Description ?? "% Describe the highlight");
72+
_ = sb.AppendLine();
73+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
74+
_ = sb.AppendLine("::::");
75+
}
76+
else
77+
{
78+
// Flattened rendering
79+
_ = sb.Append("* ");
80+
_ = sb.Append(ChangelogTextUtilities.Beautify(entry.Title));
81+
_ = sb.AppendLine();
82+
83+
// Description with proper indentation
84+
if (!string.IsNullOrWhiteSpace(entry.Description))
85+
{
86+
_ = sb.AppendLine(ChangelogTextUtilities.Indent(entry.Description));
87+
_ = sb.AppendLine();
88+
}
89+
90+
// PR/Issue links with "For more information" pattern
91+
RenderPrIssueLinks(sb, entry, entryRepo, entryOwner, entryHideLinks);
92+
}
93+
7194
if (shouldHide)
7295
_ = sb.AppendLine("-->");
7396
}

0 commit comments

Comments
 (0)