From a2401ce1548db2f0825705e4ba765a89c3f66875 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 24 Feb 2026 11:37:39 -0800 Subject: [PATCH 1/2] Fix changelog add --no-extract-issues and --no-extract-release-notes --- docs/cli/release/changelog-add.md | 11 ++-- .../Creation/ChangelogCreationService.cs | 20 ++++-- .../Creation/IssueInfoProcessor.cs | 4 +- .../Creation/PrInfoProcessor.cs | 4 +- .../docs-builder/Commands/ChangelogCommand.cs | 9 +-- .../Create/ReleaseNoteExtractionTests.cs | 65 +++++++++++++++++++ 6 files changed, 95 insertions(+), 18 deletions(-) diff --git a/docs/cli/release/changelog-add.md b/docs/cli/release/changelog-add.md index d9e889182..ae4fa76a5 100644 --- a/docs/cli/release/changelog-add.md +++ b/docs/cli/release/changelog-add.md @@ -27,7 +27,7 @@ docs-builder changelog add [options...] [-h|--help] `--no-extract-release-notes` : Optional: Turn off extraction of release notes from PR descriptions. -: By default, the extractor looks for content in various formats in the PR description: +: The extractor looks for content in various formats in the PR description: : - `Release Notes: ...` : - `Release-Notes: ...` : - `release notes: ...` @@ -36,6 +36,7 @@ docs-builder changelog add [options...] [-h|--help] : - `## Release Note` (as a markdown header) : Short release notes (≤120 characters, single line) are used as the changelog title (only if `--title` is not explicitly provided). : Long release notes (>120 characters or multi-line) are used as the changelog description (only if `--description` is not explicitly provided). +: If `--no-extract-release-notes` is not specified, the behavior is determined by the `extract.release_notes` changelog configuration setting. `--feature-id ` : Optional: Feature flag ID @@ -49,7 +50,7 @@ docs-builder changelog add [options...] [-h|--help] `--issues ` : Optional: Issue URL(s) or number(s) (comma-separated), or a path to a newline-delimited file containing issue URLs or numbers. Can be specified multiple times. -: Each occurrence can be either comma-separated issues (e.g., `--issues "https://github.com/owner/repo/issues/123,456"`) or a file path (e.g., `--issues /path/to/file.txt`). +: Each occurrence can be either comma-separated issues (for example `--issues "https://github.com/owner/repo/issues/123,456"`) or a file path (for example `--issues /path/to/file.txt`). : When specifying issues directly, provide comma-separated values. : When specifying a file path, provide a single value that points to a newline-delimited file. : If `--owner` and `--repo` are provided, issue numbers can be used instead of URLs. @@ -60,7 +61,7 @@ docs-builder changelog add [options...] [-h|--help] : Optional: Turn off extraction of linked references. : When using `--prs`: turns off extraction of linked issues from the PR body (for example, "Fixes #123"). : When using `--issues`: turns off extraction of linked PRs from the issue body (for example, "Fixed by #123"). -: By default, linked references are extracted in both cases. +: If `--no-extract-issues` is not specified, the behavior is determined by the `extract.issues` changelog configuration setting. `--output ` : Optional: Output directory for the changelog fragment. Defaults to current directory. @@ -75,7 +76,7 @@ docs-builder changelog add [options...] [-h|--help] `--prs ` : Optional: Pull request URLs or numbers (comma-separated), or a path to a newline-delimited file containing PR URLs or numbers. Can be specified multiple times. -: Each occurrence can be either comma-separated PRs (e.g., `--prs "https://github.com/owner/repo/pull/123,6789"`) or a file path (e.g., `--prs /path/to/file.txt`). +: Each occurrence can be either comma-separated PRs (for example `--prs "https://github.com/owner/repo/pull/123,6789"`) or a file path (for example `--prs /path/to/file.txt`). : When specifying PRs directly, provide comma-separated values. : When specifying a file path, provide a single value that points to a newline-delimited file. : If `--owner` and `--repo` are provided, PR numbers can be used instead of URLs. @@ -90,7 +91,7 @@ docs-builder changelog add [options...] [-h|--help] `--strip-title-prefix` : Optional: When used with `--prs`, remove square brackets and text within them from the beginning of PR titles, and also remove a colon if it follows the closing bracket. : For example, if a PR title is `"[Attack discovery]: Improves Attack discovery hallucination detection"`, the changelog title will be `"Improves Attack discovery hallucination detection"`. -: Multiple square bracket prefixes are also supported (e.g., `"[Discover][ESQL] Fix filtering by multiline string fields"` becomes `"Fix filtering by multiline string fields"`). +: Multiple square bracket prefixes are also supported (for example `"[Discover][ESQL] Fix filtering by multiline string fields"` becomes `"Fix filtering by multiline string fields"`). : This option applies only when the title is derived from the PR (when `--title` is not explicitly provided). `--subtype ` diff --git a/src/services/Elastic.Changelog/Creation/ChangelogCreationService.cs b/src/services/Elastic.Changelog/Creation/ChangelogCreationService.cs index 6534d8977..5f54939e6 100644 --- a/src/services/Elastic.Changelog/Creation/ChangelogCreationService.cs +++ b/src/services/Elastic.Changelog/Creation/ChangelogCreationService.cs @@ -38,8 +38,15 @@ public record CreateChangelogArguments public bool UsePrNumber { get; init; } public bool UseIssueNumber { get; init; } public bool StripTitlePrefix { get; init; } - public bool ExtractReleaseNotes { get; init; } - public bool ExtractIssues { get; init; } + /// + /// Whether to extract release notes from PR/issue descriptions. null = use config default. + /// + public bool? ExtractReleaseNotes { get; init; } + + /// + /// Whether to extract linked issues/PRs from PR/issue body. null = use config default. + /// + public bool? ExtractIssues { get; init; } } /// @@ -122,9 +129,12 @@ public async Task CreateChangelog(IDiagnosticsCollector collector, CreateC } } - private static CreateChangelogArguments ApplyConfigDefaults(CreateChangelogArguments input, ChangelogConfiguration _) => - // Config defaults are already handled by CLI layer, but this ensures service layer has proper defaults too - input; + private static CreateChangelogArguments ApplyConfigDefaults(CreateChangelogArguments input, ChangelogConfiguration config) => + input with + { + ExtractReleaseNotes = input.ExtractReleaseNotes ?? config.Extract.ReleaseNotes, + ExtractIssues = input.ExtractIssues ?? config.Extract.Issues + }; /// /// Infers products from configuration defaults or git repository name. diff --git a/src/services/Elastic.Changelog/Creation/IssueInfoProcessor.cs b/src/services/Elastic.Changelog/Creation/IssueInfoProcessor.cs index e0c1e23ea..138975aba 100644 --- a/src/services/Elastic.Changelog/Creation/IssueInfoProcessor.cs +++ b/src/services/Elastic.Changelog/Creation/IssueInfoProcessor.cs @@ -90,7 +90,7 @@ public async Task ProcessIssueAsync( { var derived = new DerivedPrFields(); - if (input.ExtractReleaseNotes) + if (input.ExtractReleaseNotes ?? false) { var (releaseNoteTitle, releaseNoteDescription) = ReleaseNotesExtractor.ExtractReleaseNotes(issueInfo.Body); @@ -176,7 +176,7 @@ public async Task ProcessIssueAsync( : [issueUrl]; // Extract linked PRs from issue body - if (input.ExtractIssues && issueInfo.LinkedPrs.Count > 0) + if ((input.ExtractIssues ?? false) && issueInfo.LinkedPrs.Count > 0) { derived.Prs = issueInfo.LinkedPrs.ToArray(); logger.LogInformation("Extracted {Count} linked PRs from issue body: {Prs}", diff --git a/src/services/Elastic.Changelog/Creation/PrInfoProcessor.cs b/src/services/Elastic.Changelog/Creation/PrInfoProcessor.cs index 4364c71fd..0b16cd59c 100644 --- a/src/services/Elastic.Changelog/Creation/PrInfoProcessor.cs +++ b/src/services/Elastic.Changelog/Creation/PrInfoProcessor.cs @@ -93,7 +93,7 @@ public async Task ProcessPrAsync( var derived = new DerivedPrFields(); // Extract release notes from PR body if requested - if (input.ExtractReleaseNotes) + if (input.ExtractReleaseNotes ?? false) { var (releaseNoteTitle, releaseNoteDescription) = ReleaseNotesExtractor.ExtractReleaseNotes(prInfo.Body); @@ -181,7 +181,7 @@ public async Task ProcessPrAsync( logger.LogDebug("Using explicitly provided highlight value, ignoring PR labels"); // Extract linked issues from PR body if config enabled and issues not provided - if (input.ExtractIssues && (input.Issues == null || input.Issues.Length == 0)) + if ((input.ExtractIssues ?? false) && (input.Issues == null || input.Issues.Length == 0)) { if (prInfo.LinkedIssues.Count > 0) { diff --git a/src/tooling/docs-builder/Commands/ChangelogCommand.cs b/src/tooling/docs-builder/Commands/ChangelogCommand.cs index 7329b9651..5288cc545 100644 --- a/src/tooling/docs-builder/Commands/ChangelogCommand.cs +++ b/src/tooling/docs-builder/Commands/ChangelogCommand.cs @@ -298,8 +298,9 @@ public async Task Create( parsedPrs = allPrs.ToArray(); } - var shouldExtractReleaseNotes = !noExtractReleaseNotes; - var shouldExtractIssues = !noExtractIssues; + // null = use config default; explicit false when --no-extract-* passed + var extractReleaseNotes = noExtractReleaseNotes ? false : (bool?)null; + var extractIssues = noExtractIssues ? false : (bool?)null; // Parse issues: handle both comma-separated values and file paths (mirrors PR parsing) string[]? parsedIssues = null; @@ -378,8 +379,8 @@ public async Task Create( UsePrNumber = usePrNumber, UseIssueNumber = useIssueNumber, StripTitlePrefix = stripTitlePrefix, - ExtractReleaseNotes = shouldExtractReleaseNotes, - ExtractIssues = shouldExtractIssues + ExtractReleaseNotes = extractReleaseNotes, + ExtractIssues = extractIssues }; serviceInvoker.AddCommand(service, input, diff --git a/tests/Elastic.Changelog.Tests/Changelogs/Create/ReleaseNoteExtractionTests.cs b/tests/Elastic.Changelog.Tests/Changelogs/Create/ReleaseNoteExtractionTests.cs index 6e7c4ed5c..57c090d7d 100644 --- a/tests/Elastic.Changelog.Tests/Changelogs/Create/ReleaseNoteExtractionTests.cs +++ b/tests/Elastic.Changelog.Tests/Changelogs/Create/ReleaseNoteExtractionTests.cs @@ -404,4 +404,69 @@ public async Task CreateChangelog_WithExtractReleaseNotes_ExplicitDescription_Ta yamlContent.Should().Contain("description: Custom description"); yamlContent.Should().NotContain(longReleaseNote); } + + [Fact] + public async Task CreateChangelog_WhenExtractNotSpecifiedByCli_UsesConfigExtractReleaseNotes() + { + // When CLI does not pass --no-extract-release-notes, config extract.release_notes applies. + // Config has release_notes: false, so PR title is used instead of extracted release note. + + var prInfo = new GitHubPrInfo + { + Title = "Implement new aggregation API", + Body = "## Summary\n\nThis PR adds a new feature.\n\nRelease Notes: Adds support for new aggregation types", + Labels = ["type:feature"] + }; + + A.CallTo(() => MockGitHubService.FetchPrInfoAsync( + "https://github.com/elastic/elasticsearch/pull/12345", + null, + null, + A._)) + .Returns(prInfo); + + // language=yaml + var configContent = + """ + extract: + release_notes: false + pivot: + types: + feature: "type:feature" + bug-fix: + breaking-change: + lifecycles: + - preview + - beta + - ga + """; + var configPath = await CreateConfigDirectory(configContent); + + var service = CreateService(); + + var input = new CreateChangelogArguments + { + Prs = ["https://github.com/elastic/elasticsearch/pull/12345"], + Products = [new ProductArgument { Product = "elasticsearch", Target = "9.2.0", Lifecycle = "ga" }], + Config = configPath, + Output = CreateOutputDirectory(), + ExtractReleaseNotes = null // CLI did not specify; config default applies + }; + + // Act + var result = await service.CreateChangelog(Collector, input, TestContext.Current.CancellationToken); + + // Assert - config says don't extract, so PR title is used + result.Should().BeTrue(); + Collector.Errors.Should().Be(0); + + var outputDir = input.Output ?? FileSystem.Directory.GetCurrentDirectory(); + FileSystem.Directory.CreateDirectory(outputDir); + var files = FileSystem.Directory.GetFiles(outputDir, "*.yaml"); + files.Should().HaveCount(1); + + var yamlContent = await FileSystem.File.ReadAllTextAsync(files[0], TestContext.Current.CancellationToken); + yamlContent.Should().Contain("title: Implement new aggregation API"); + yamlContent.Should().NotContain("Adds support for new aggregation types"); + } } From 6488e29c1bddc1c4da91ecfaf3a34b431608c170 Mon Sep 17 00:00:00 2001 From: lcawl Date: Tue, 24 Feb 2026 12:16:05 -0800 Subject: [PATCH 2/2] Improve docs --- docs/cli/release/changelog-add.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cli/release/changelog-add.md b/docs/cli/release/changelog-add.md index ae4fa76a5..2e8ee3d7b 100644 --- a/docs/cli/release/changelog-add.md +++ b/docs/cli/release/changelog-add.md @@ -36,7 +36,7 @@ docs-builder changelog add [options...] [-h|--help] : - `## Release Note` (as a markdown header) : Short release notes (≤120 characters, single line) are used as the changelog title (only if `--title` is not explicitly provided). : Long release notes (>120 characters or multi-line) are used as the changelog description (only if `--description` is not explicitly provided). -: If `--no-extract-release-notes` is not specified, the behavior is determined by the `extract.release_notes` changelog configuration setting. +: By default, the behavior is determined by the `extract.release_notes` changelog configuration setting. `--feature-id ` : Optional: Feature flag ID @@ -61,7 +61,7 @@ docs-builder changelog add [options...] [-h|--help] : Optional: Turn off extraction of linked references. : When using `--prs`: turns off extraction of linked issues from the PR body (for example, "Fixes #123"). : When using `--issues`: turns off extraction of linked PRs from the issue body (for example, "Fixed by #123"). -: If `--no-extract-issues` is not specified, the behavior is determined by the `extract.issues` changelog configuration setting. +: By default, the behavior is determined by the `extract.issues` changelog configuration setting. `--output ` : Optional: Output directory for the changelog fragment. Defaults to current directory.