diff --git a/docs/cli/release/changelog-add.md b/docs/cli/release/changelog-add.md index 41a013bc4..cf5478333 100644 --- a/docs/cli/release/changelog-add.md +++ b/docs/cli/release/changelog-add.md @@ -68,7 +68,7 @@ docs-builder changelog add [options...] [-h|--help] : If specified, `--title` can be derived from the PR. : If mappings are configured, `--areas` and `--type` can also be derived from the PR. : Creates one changelog file per PR. -: If `add_blockers` are configured in the changelog configuration file and a PR has a blocking label for any product in `--products`, that PR is skipped and no changelog file is created for it. +: If there are `block ... create` definitions in the changelog configuration file and a PR has a blocking label for any product in `--products`, that PR is skipped and no changelog file is created for it. `--repo ` : Optional: GitHub repository name (used when `--pr` is just a number). diff --git a/docs/cli/release/changelog-render.md b/docs/cli/release/changelog-render.md index 8743ee691..9062b7b2c 100644 --- a/docs/cli/release/changelog-render.md +++ b/docs/cli/release/changelog-render.md @@ -16,7 +16,7 @@ docs-builder changelog render [options...] [-h|--help] `--config ` : Optional: Path to the changelog.yml configuration file. : Defaults to `docs/changelog.yml`. -: This configuration file is where the command looks for `render_blockers` details. +: This configuration file is where the command looks `block ... publish` definitions. `--hide-features ` : Optional: Filter by feature IDs (comma-separated), or a path to a newline-delimited file containing feature IDs. Can be specified multiple times. @@ -54,7 +54,7 @@ docs-builder changelog render [options...] [-h|--help] : Defaults to the version in the first bundle. : If the string contains spaces, they are replaced with dashes when used in directory names and anchors. -You can configure `render_blockers` in your `changelog.yml` configuration file to automatically block changelog entries from being rendered based on their products, areas, and/or types. +You can configure `block` definitions in your `changelog.yml` configuration file to automatically comment out changelog entries based on their products, areas, and/or types. For more information, refer to [](/contribute/changelog.md#example-block-label). ## Output formats diff --git a/src/services/Elastic.Changelog/Rendering/Asciidoc/BreakingChangesAsciidocRenderer.cs b/src/services/Elastic.Changelog/Rendering/Asciidoc/BreakingChangesAsciidocRenderer.cs index 140e9c93c..b0acff1cb 100644 --- a/src/services/Elastic.Changelog/Rendering/Asciidoc/BreakingChangesAsciidocRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Asciidoc/BreakingChangesAsciidocRenderer.cs @@ -23,10 +23,15 @@ public override void Render(IReadOnlyCollection entries, Changel foreach (var group in groupedEntries) { + // Check if all entries in this group are hidden + var allEntriesHidden = group.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (context.Subsections && !string.IsNullOrWhiteSpace(group.Key)) { var header = ChangelogTextUtilities.FormatSubtypeHeader(group.Key); - _ = sb.AppendLine(CultureInfo.InvariantCulture, $"**{header}**"); + var headerLine = allEntriesHidden ? $"// **{header}**" : $"**{header}**"; + _ = sb.AppendLine(headerLine); _ = sb.AppendLine(); } diff --git a/src/services/Elastic.Changelog/Rendering/Asciidoc/DeprecationsAsciidocRenderer.cs b/src/services/Elastic.Changelog/Rendering/Asciidoc/DeprecationsAsciidocRenderer.cs index 6df3181bf..9b616120b 100644 --- a/src/services/Elastic.Changelog/Rendering/Asciidoc/DeprecationsAsciidocRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Asciidoc/DeprecationsAsciidocRenderer.cs @@ -20,10 +20,15 @@ public override void Render(IReadOnlyCollection entries, Changel foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + var componentName = !string.IsNullOrWhiteSpace(areaGroup.Key) ? areaGroup.Key : "General"; var formattedComponent = ChangelogTextUtilities.FormatAreaHeader(componentName); - _ = sb.AppendLine(CultureInfo.InvariantCulture, $"{formattedComponent}::"); + var headerLine = allEntriesHidden ? $"// {formattedComponent}::" : $"{formattedComponent}::"; + _ = sb.AppendLine(headerLine); _ = sb.AppendLine(); foreach (var entry in areaGroup) diff --git a/src/services/Elastic.Changelog/Rendering/Asciidoc/EntriesByAreaAsciidocRenderer.cs b/src/services/Elastic.Changelog/Rendering/Asciidoc/EntriesByAreaAsciidocRenderer.cs index 5b68210c3..0d4ba1610 100644 --- a/src/services/Elastic.Changelog/Rendering/Asciidoc/EntriesByAreaAsciidocRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Asciidoc/EntriesByAreaAsciidocRenderer.cs @@ -22,10 +22,15 @@ public override void Render(IReadOnlyCollection entries, Changel foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + var componentName = !string.IsNullOrWhiteSpace(areaGroup.Key) ? areaGroup.Key : "General"; var formattedComponent = ChangelogTextUtilities.FormatAreaHeader(componentName); - _ = sb.AppendLine(CultureInfo.InvariantCulture, $"{formattedComponent}::"); + var headerLine = allEntriesHidden ? $"// {formattedComponent}::" : $"{formattedComponent}::"; + _ = sb.AppendLine(headerLine); _ = sb.AppendLine(); foreach (var entry in areaGroup) diff --git a/src/services/Elastic.Changelog/Rendering/Asciidoc/KnownIssuesAsciidocRenderer.cs b/src/services/Elastic.Changelog/Rendering/Asciidoc/KnownIssuesAsciidocRenderer.cs index e85568342..9f02be72d 100644 --- a/src/services/Elastic.Changelog/Rendering/Asciidoc/KnownIssuesAsciidocRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Asciidoc/KnownIssuesAsciidocRenderer.cs @@ -20,10 +20,15 @@ public override void Render(IReadOnlyCollection entries, Changel foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + var componentName = !string.IsNullOrWhiteSpace(areaGroup.Key) ? areaGroup.Key : "General"; var formattedComponent = ChangelogTextUtilities.FormatAreaHeader(componentName); - _ = sb.AppendLine(CultureInfo.InvariantCulture, $"{formattedComponent}::"); + var headerLine = allEntriesHidden ? $"// {formattedComponent}::" : $"{formattedComponent}::"; + _ = sb.AppendLine(headerLine); _ = sb.AppendLine(); foreach (var entry in areaGroup) diff --git a/src/services/Elastic.Changelog/Rendering/ChangelogRenderingService.cs b/src/services/Elastic.Changelog/Rendering/ChangelogRenderingService.cs index deec9c115..730fc3d43 100644 --- a/src/services/Elastic.Changelog/Rendering/ChangelogRenderingService.cs +++ b/src/services/Elastic.Changelog/Rendering/ChangelogRenderingService.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.IO.Abstractions; +using System.Linq; using System.Text.Json.Serialization; using Elastic.Changelog.Configuration; using Elastic.Documentation; @@ -122,13 +123,16 @@ Cancel ctx // Emit warnings for hidden entries EmitHiddenEntryWarnings(collector, resolvedResult.Entries, featureHidingResult.FeatureIdsToHide); + // Build render context (needed for block checking) + var context = BuildRenderContext(input, outputSetup, resolvedResult, featureHidingResult.FeatureIdsToHide, config); + + // Emit warnings for blocked entries + EmitBlockedEntryWarnings(collector, resolvedResult.Entries, context); + // Validate entry types if (!ValidateEntryTypes(collector, resolvedResult.Entries, config.Types)) return false; - // Build render context - var context = BuildRenderContext(input, outputSetup, resolvedResult, featureHidingResult.FeatureIdsToHide, config); - // Render output var renderer = new ChangelogRenderer(_fileSystem, _logger); await renderer.RenderAsync(input.FileType, context, ctx); @@ -194,6 +198,90 @@ private static void EmitHiddenEntryWarnings( } } + private static void EmitBlockedEntryWarnings( + IDiagnosticsCollector collector, + IReadOnlyList entries, + ChangelogRenderContext context) + { + if (context.Configuration?.Block == null) + return; + + var visibleEntries = entries.Where(resolved => + string.IsNullOrWhiteSpace(resolved.Entry.FeatureId) || + !context.FeatureIdsToHide.Contains(resolved.Entry.FeatureId)); + + foreach (var resolved in visibleEntries) + { + // Get product IDs for this entry + var productIds = context.EntryToBundleProducts.GetValueOrDefault(resolved.Entry, new HashSet(StringComparer.OrdinalIgnoreCase)); + if (productIds.Count == 0) + continue; + + // Check each product's block configuration + foreach (var productId in productIds) + { + var blocker = GetPublishBlockerForProduct(context.Configuration.Block, productId); + if (blocker != null && blocker.ShouldBlock(resolved.Entry)) + { + var reasons = GetBlockReasons(resolved.Entry, blocker); + var productInfo = productIds.Count > 1 ? $" for product '{productId}'" : ""; + var entryIdentifier = GetEntryIdentifier(resolved.Entry, context); + collector.EmitWarning(string.Empty, $"Changelog entry {entryIdentifier} will be commented out{productInfo} because it matches block configuration: {reasons}"); + } + } + } + } + + private static string GetEntryIdentifier(ChangelogEntry entry, ChangelogRenderContext context) + { + // Try to extract PR number if available + if (!string.IsNullOrWhiteSpace(entry.Pr)) + { + var repo = context.EntryToRepo.GetValueOrDefault(entry, context.Repo); + var prNumber = ChangelogTextUtilities.ExtractPrNumber(entry.Pr, "elastic", repo); + if (prNumber.HasValue) + return $"for PR {prNumber.Value}"; + } + + // Fall back to title if no PR is available + return $"'{entry.Title}'"; + } + + private static string GetBlockReasons(ChangelogEntry entry, PublishBlocker blocker) + { + var reasons = new List(); + + // Check if blocked by type + if (blocker.Types?.Count > 0) + { + var entryTypeName = entry.Type.ToStringFast(true); + if (blocker.Types.Any(t => t.Equals(entryTypeName, StringComparison.OrdinalIgnoreCase))) + reasons.Add($"type '{entryTypeName}'"); + } + + // Check if blocked by area + if (blocker.Areas?.Count > 0 && entry.Areas?.Count > 0) + { + var blockedAreas = entry.Areas + .Where(area => blocker.Areas.Any(blocked => blocked.Equals(area, StringComparison.OrdinalIgnoreCase))) + .ToList(); + if (blockedAreas.Count > 0) + reasons.Add($"area{(blockedAreas.Count > 1 ? "s" : "")} '{string.Join("', '", blockedAreas)}'"); + } + + return string.Join(" and ", reasons); + } + + private static PublishBlocker? GetPublishBlockerForProduct(BlockConfiguration blockConfig, string productId) + { + // Check product-specific override first + if (blockConfig.ByProduct?.TryGetValue(productId, out var productBlockers) == true) + return productBlockers.Publish; + + // Fall back to global publish blocker + return blockConfig.Publish; + } + private static bool ValidateEntryTypes( IDiagnosticsCollector collector, IReadOnlyList entries, diff --git a/src/services/Elastic.Changelog/Rendering/Markdown/BreakingChangesMarkdownRenderer.cs b/src/services/Elastic.Changelog/Rendering/Markdown/BreakingChangesMarkdownRenderer.cs index 26e2b0f09..351774057 100644 --- a/src/services/Elastic.Changelog/Rendering/Markdown/BreakingChangesMarkdownRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Markdown/BreakingChangesMarkdownRenderer.cs @@ -26,6 +26,10 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct var sb = new StringBuilder(); _ = sb.AppendLine(InvariantCulture, $"## {context.Title} [{context.Repo}-{context.TitleSlug}-breaking-changes]"); + // Check if all entries are hidden + var allEntriesHidden = breakingChanges.Count > 0 && breakingChanges.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (breakingChanges.Count > 0) { // Group by subtype if subsections are enabled, otherwise group by area @@ -35,11 +39,19 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct foreach (var group in groupedEntries) { + // Check if all entries in this group are hidden + var allGroupEntriesHidden = group.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (context.Subsections && !string.IsNullOrWhiteSpace(group.Key)) { var header = ChangelogTextUtilities.FormatSubtypeHeader(group.Key); _ = sb.AppendLine(); + if (allGroupEntriesHidden) + _ = sb.AppendLine(""); } foreach (var entry in group) @@ -70,9 +82,18 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct _ = sb.AppendLine("-->"); } } + + // Add message if all entries are hidden + if (allEntriesHidden) + { + _ = sb.AppendLine(); + _ = sb.AppendLine("_There are no breaking changes associated with this release._"); + } } else - _ = sb.AppendLine("_No breaking changes._"); + { + _ = sb.AppendLine("_There are no breaking changes associated with this release._"); + } await WriteOutputFileAsync(context.OutputDir, context.TitleSlug, sb.ToString(), ctx); } diff --git a/src/services/Elastic.Changelog/Rendering/Markdown/DeprecationsMarkdownRenderer.cs b/src/services/Elastic.Changelog/Rendering/Markdown/DeprecationsMarkdownRenderer.cs index 6a1ee54c3..bc494db9f 100644 --- a/src/services/Elastic.Changelog/Rendering/Markdown/DeprecationsMarkdownRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Markdown/DeprecationsMarkdownRenderer.cs @@ -26,6 +26,10 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct var sb = new StringBuilder(); _ = sb.AppendLine(InvariantCulture, $"## {context.Title} [{context.Repo}-{context.TitleSlug}-deprecations]"); + // Check if all entries are hidden + var allEntriesHidden = deprecations.Count > 0 && deprecations.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (deprecations.Count > 0) { var groupedByArea = context.Subsections @@ -33,11 +37,19 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct : deprecations.GroupBy(ChangelogRenderUtilities.GetComponent).ToList(); foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allGroupEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (context.Subsections && !string.IsNullOrWhiteSpace(areaGroup.Key)) { var header = ChangelogTextUtilities.FormatAreaHeader(areaGroup.Key); _ = sb.AppendLine(); + if (allGroupEntriesHidden) + _ = sb.AppendLine(""); } foreach (var entry in areaGroup) @@ -68,9 +80,18 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct _ = sb.AppendLine("-->"); } } + + // Add message if all entries are hidden + if (allEntriesHidden) + { + _ = sb.AppendLine(); + _ = sb.AppendLine("_There are no deprecations associated with this release._"); + } } else - _ = sb.AppendLine("_No deprecations._"); + { + _ = sb.AppendLine("_There are no deprecations associated with this release._"); + } await WriteOutputFileAsync(context.OutputDir, context.TitleSlug, sb.ToString(), ctx); } diff --git a/src/services/Elastic.Changelog/Rendering/Markdown/IndexMarkdownRenderer.cs b/src/services/Elastic.Changelog/Rendering/Markdown/IndexMarkdownRenderer.cs index f0279a4fc..227d53ba3 100644 --- a/src/services/Elastic.Changelog/Rendering/Markdown/IndexMarkdownRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Markdown/IndexMarkdownRenderer.cs @@ -54,20 +54,36 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct var hasAnyEntries = features.Count > 0 || enhancements.Count > 0 || security.Count > 0 || bugFixes.Count > 0 || docs.Count > 0 || regressions.Count > 0 || other.Count > 0; + // Helper to check if all entries in a collection are hidden + bool AllEntriesHidden(IReadOnlyCollection entries) => + entries.Count > 0 && entries.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + + // Check if each category has visible entries + var hasVisibleFeatures = (features.Count > 0 || enhancements.Count > 0) && + !(AllEntriesHidden(features) && AllEntriesHidden(enhancements)); + var hasVisibleFixes = (security.Count > 0 || bugFixes.Count > 0) && + !(AllEntriesHidden(security) && AllEntriesHidden(bugFixes)); + var hasVisibleDocs = docs.Count > 0 && !AllEntriesHidden(docs); + var hasVisibleRegressions = regressions.Count > 0 && !AllEntriesHidden(regressions); + var hasVisibleOther = other.Count > 0 && !AllEntriesHidden(other); + + var hasAnyVisibleEntries = hasVisibleFeatures || hasVisibleFixes || hasVisibleDocs || hasVisibleRegressions || hasVisibleOther; + if (hasAnyEntries) { if (features.Count > 0 || enhancements.Count > 0) { - _ = sb.AppendLine(InvariantCulture, $"### Features and enhancements [{context.Repo}-{context.TitleSlug}-features-enhancements]"); var combined = features.Concat(enhancements).ToList(); + _ = sb.AppendLine(InvariantCulture, $"### Features and enhancements [{context.Repo}-{context.TitleSlug}-features-enhancements]"); RenderEntriesByArea(sb, combined, context); } if (security.Count > 0 || bugFixes.Count > 0) { + var combined = security.Concat(bugFixes).ToList(); _ = sb.AppendLine(); _ = sb.AppendLine(InvariantCulture, $"### Fixes [{context.Repo}-{context.TitleSlug}-fixes]"); - var combined = security.Concat(bugFixes).ToList(); RenderEntriesByArea(sb, combined, context); } @@ -91,9 +107,18 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct _ = sb.AppendLine(InvariantCulture, $"### Other changes [{context.Repo}-{context.TitleSlug}-other]"); RenderEntriesByArea(sb, other, context); } + + // Add message if all entries are hidden + if (!hasAnyVisibleEntries) + { + _ = sb.AppendLine(); + _ = sb.AppendLine("_There are no new features, enhancements, or fixes associated with this release._"); + } } else - _ = sb.AppendLine("_No new features, enhancements, or fixes._"); + { + _ = sb.AppendLine("_There are no new features, enhancements, or fixes associated with this release._"); + } await WriteOutputFileAsync(context.OutputDir, context.TitleSlug, sb.ToString(), ctx); } @@ -108,10 +133,16 @@ private static void RenderEntriesByArea( : entries.GroupBy(ChangelogRenderUtilities.GetComponent).ToList(); foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (context.Subsections && !string.IsNullOrWhiteSpace(areaGroup.Key)) { var header = ChangelogTextUtilities.FormatAreaHeader(areaGroup.Key); _ = sb.AppendLine(); + if (allEntriesHidden) + _ = sb.Append("% "); _ = sb.AppendLine(InvariantCulture, $"**{header}**"); } diff --git a/src/services/Elastic.Changelog/Rendering/Markdown/KnownIssuesMarkdownRenderer.cs b/src/services/Elastic.Changelog/Rendering/Markdown/KnownIssuesMarkdownRenderer.cs index c67938e46..3942d5c86 100644 --- a/src/services/Elastic.Changelog/Rendering/Markdown/KnownIssuesMarkdownRenderer.cs +++ b/src/services/Elastic.Changelog/Rendering/Markdown/KnownIssuesMarkdownRenderer.cs @@ -26,6 +26,10 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct var sb = new StringBuilder(); _ = sb.AppendLine(InvariantCulture, $"## {context.Title} [{context.Repo}-{context.TitleSlug}-known-issues]"); + // Check if all entries are hidden + var allEntriesHidden = knownIssues.Count > 0 && knownIssues.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (knownIssues.Count > 0) { var groupedByArea = context.Subsections @@ -33,11 +37,19 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct : knownIssues.GroupBy(ChangelogRenderUtilities.GetComponent).ToList(); foreach (var areaGroup in groupedByArea) { + // Check if all entries in this area group are hidden + var allGroupEntriesHidden = areaGroup.All(entry => + ChangelogRenderUtilities.ShouldHideEntry(entry, context.FeatureIdsToHide, context)); + if (context.Subsections && !string.IsNullOrWhiteSpace(areaGroup.Key)) { var header = ChangelogTextUtilities.FormatAreaHeader(areaGroup.Key); _ = sb.AppendLine(); + if (allGroupEntriesHidden) + _ = sb.AppendLine(""); } foreach (var entry in areaGroup) @@ -68,9 +80,18 @@ public override async Task RenderAsync(ChangelogRenderContext context, Cancel ct _ = sb.AppendLine("-->"); } } + + // Add message if all entries are hidden + if (allEntriesHidden) + { + _ = sb.AppendLine(); + _ = sb.AppendLine("_There are no known issues associated with this release._"); + } } else - _ = sb.AppendLine("_No known issues._"); + { + _ = sb.AppendLine("_There are no known issues associated with this release._"); + } await WriteOutputFileAsync(context.OutputDir, context.TitleSlug, sb.ToString(), ctx); } diff --git a/tests/Elastic.Changelog.Tests/Changelogs/Render/BlockConfigurationTests.cs b/tests/Elastic.Changelog.Tests/Changelogs/Render/BlockConfigurationTests.cs index 5b9970196..285bf5c7c 100644 --- a/tests/Elastic.Changelog.Tests/Changelogs/Render/BlockConfigurationTests.cs +++ b/tests/Elastic.Changelog.Tests/Changelogs/Render/BlockConfigurationTests.cs @@ -141,7 +141,7 @@ public async Task RenderChangelogs_WithBlockedType_CommentsOutMatchingEntries() var changelogDir = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString()); FileSystem.Directory.CreateDirectory(changelogDir); - // Create changelog with blocked type + // Create changelog with blocked type (blocked by area) // language=yaml var changelog1 = """ @@ -150,27 +150,46 @@ public async Task RenderChangelogs_WithBlockedType_CommentsOutMatchingEntries() products: - product: cloud-serverless target: 2026-01-26 + areas: + - Allocation pr: https://github.com/elastic/elasticsearch/pull/100 description: This deprecation should be blocked """; - // Create changelog with non-blocked type + // Create visible deprecation (not blocked - different area) // language=yaml var changelog2 = + """ + title: Visible deprecation + type: deprecation + products: + - product: cloud-serverless + target: 2026-01-26 + areas: + - Search + pr: https://github.com/elastic/elasticsearch/pull/101 + description: This deprecation should be visible + """; + + // Create changelog with non-blocked type + // language=yaml + var changelog3 = """ title: Visible feature type: feature products: - product: cloud-serverless target: 2026-01-26 - pr: https://github.com/elastic/elasticsearch/pull/101 + pr: https://github.com/elastic/elasticsearch/pull/102 description: This feature should be visible """; var changelogFile1 = FileSystem.Path.Combine(changelogDir, "1755268130-blocked.yaml"); - var changelogFile2 = FileSystem.Path.Combine(changelogDir, "1755268140-visible.yaml"); + var changelogFile2 = FileSystem.Path.Combine(changelogDir, "1755268140-visible-deprecation.yaml"); + var changelogFile3 = FileSystem.Path.Combine(changelogDir, "1755268150-visible-feature.yaml"); await FileSystem.File.WriteAllTextAsync(changelogFile1, changelog1, TestContext.Current.CancellationToken); await FileSystem.File.WriteAllTextAsync(changelogFile2, changelog2, TestContext.Current.CancellationToken); + await FileSystem.File.WriteAllTextAsync(changelogFile3, changelog3, TestContext.Current.CancellationToken); // Create bundle file var bundleDir = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString()); @@ -188,12 +207,16 @@ public async Task RenderChangelogs_WithBlockedType_CommentsOutMatchingEntries() name: 1755268130-blocked.yaml checksum: {ComputeSha1(changelog1)} - file: - name: 1755268140-visible.yaml + name: 1755268140-visible-deprecation.yaml checksum: {ComputeSha1(changelog2)} + - file: + name: 1755268150-visible-feature.yaml + checksum: {ComputeSha1(changelog3)} """; await FileSystem.File.WriteAllTextAsync(bundleFile, bundleContent, TestContext.Current.CancellationToken); - // Create config with block configuration + // Create config with block configuration - block only Allocation area (not all deprecations) + // This will block the deprecation in Allocation area but not the one in Search area var configDir = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString()); FileSystem.Directory.CreateDirectory(configDir); var configFile = FileSystem.Path.Combine(configDir, "changelog.yml"); @@ -214,8 +237,8 @@ public async Task RenderChangelogs_WithBlockedType_CommentsOutMatchingEntries() product: cloud-serverless: publish: - types: - - deprecation + areas: + - Allocation """; await FileSystem.File.WriteAllTextAsync(configFile, configContent, TestContext.Current.CancellationToken); @@ -245,10 +268,13 @@ public async Task RenderChangelogs_WithBlockedType_CommentsOutMatchingEntries() FileSystem.File.Exists(deprecationsFile).Should().BeTrue(); var deprecationsContent = await FileSystem.File.ReadAllTextAsync(deprecationsFile, TestContext.Current.CancellationToken); - // Should use block comments + // Should use block comments for blocked entry deprecationsContent.Should().Contain(""); deprecationsContent.Should().Contain("Blocked deprecation"); + // Visible entry should not be commented + deprecationsContent.Should().Contain("Visible deprecation"); + deprecationsContent.Should().NotContain("", StringComparison.Ordinal); @@ -546,7 +572,7 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme FileSystem.Directory.CreateDirectory(changelogDir); // language=yaml - var changelog = + var changelog1 = """ title: Blocked Allocation breaking change type: breaking-change @@ -561,8 +587,27 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme action: Update your code """; - var changelogFile = FileSystem.Path.Combine(changelogDir, "1755268130-breaking.yaml"); - await FileSystem.File.WriteAllTextAsync(changelogFile, changelog, TestContext.Current.CancellationToken); + // Create visible breaking change (not blocked) + // language=yaml + var changelog2 = + """ + title: Visible Search breaking change + type: breaking-change + products: + - product: cloud-serverless + target: 2026-01-26 + areas: + - Search + pr: https://github.com/elastic/elasticsearch/pull/101 + description: This breaking change should be visible + impact: Users will be affected + action: Update your code + """; + + var changelogFile1 = FileSystem.Path.Combine(changelogDir, "1755268130-blocked-breaking.yaml"); + var changelogFile2 = FileSystem.Path.Combine(changelogDir, "1755268140-visible-breaking.yaml"); + await FileSystem.File.WriteAllTextAsync(changelogFile1, changelog1, TestContext.Current.CancellationToken); + await FileSystem.File.WriteAllTextAsync(changelogFile2, changelog2, TestContext.Current.CancellationToken); var bundleDir = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString()); FileSystem.Directory.CreateDirectory(bundleDir); @@ -576,8 +621,11 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme target: 2026-01-26 entries: - file: - name: 1755268130-breaking.yaml - checksum: {ComputeSha1(changelog)} + name: 1755268130-blocked-breaking.yaml + checksum: {ComputeSha1(changelog1)} + - file: + name: 1755268140-visible-breaking.yaml + checksum: {ComputeSha1(changelog2)} """; await FileSystem.File.WriteAllTextAsync(bundleFile, bundleContent, TestContext.Current.CancellationToken); @@ -595,6 +643,7 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme breaking-change: areas: Allocation: + Search: lifecycles: - preview - beta @@ -634,7 +683,7 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme FileSystem.File.Exists(breakingFile).Should().BeTrue(); var breakingContent = await FileSystem.File.ReadAllTextAsync(breakingFile, TestContext.Current.CancellationToken); - // Should use block comments + // Should use block comments for blocked entry breakingContent.Should().Contain(""); breakingContent.Should().Contain("Blocked Allocation breaking change"); @@ -643,6 +692,9 @@ public async Task RenderChangelogs_WithBlockedArea_BreakingChange_UsesBlockComme var commentEnd = breakingContent.IndexOf("-->", StringComparison.Ordinal); commentStart.Should().BeLessThan(commentEnd); breakingContent.Substring(commentStart, commentEnd - commentStart).Should().Contain("Blocked Allocation breaking change"); + // Visible entry should not be commented + breakingContent.Should().Contain("Visible Search breaking change"); + breakingContent.Should().NotContain(" + // Should use block comments for blocked entry knownIssuesContent.Should().Contain(""); knownIssuesContent.Should().Contain("Blocked Allocation known issue"); @@ -751,6 +826,9 @@ public async Task RenderChangelogs_WithBlockedArea_KnownIssue_UsesBlockComments( var commentEnd = knownIssuesContent.IndexOf("-->", StringComparison.Ordinal); commentStart.Should().BeLessThan(commentEnd); knownIssuesContent.Substring(commentStart, commentEnd - commentStart).Should().Contain("Blocked Allocation known issue"); + // Visible entry should not be commented + knownIssuesContent.Should().Contain("Visible Search known issue"); + knownIssuesContent.Should().NotContain("