diff --git a/src/services/Elastic.Changelog/Bundling/ChangelogBundlingService.cs b/src/services/Elastic.Changelog/Bundling/ChangelogBundlingService.cs index 3a89a13ab..c72ca461c 100644 --- a/src/services/Elastic.Changelog/Bundling/ChangelogBundlingService.cs +++ b/src/services/Elastic.Changelog/Bundling/ChangelogBundlingService.cs @@ -29,7 +29,11 @@ public record BundleChangelogsArguments public bool All { get; init; } public IReadOnlyList? InputProducts { get; init; } public IReadOnlyList? OutputProducts { get; init; } - public bool Resolve { get; init; } + /// + /// Whether to resolve (copy contents of each changelog file into the entries array). + /// null = use config default; true = --resolve; false = --no-resolve. + /// + public bool? Resolve { get; init; } public string[]? Prs { get; init; } public string[]? Issues { get; init; } public string? Owner { get; init; } @@ -175,7 +179,7 @@ public async Task BundleChangelogs(IDiagnosticsCollector collector, Bundle collector, matchResult.Entries, input.OutputProducts, - input.Resolve, + input.Resolve ?? false, input.Repo, featureHidingResult.FeatureIdsToHide ); @@ -336,8 +340,8 @@ private static BundleChangelogsArguments ApplyConfigDefaults(BundleChangelogsArg if (string.IsNullOrWhiteSpace(output) && !string.IsNullOrWhiteSpace(config.Bundle.OutputDirectory)) output = Path.Combine(config.Bundle.OutputDirectory, "changelog-bundle.yaml"); - // Apply resolve default if not specified by CLI - var resolve = input.Resolve || config.Bundle.Resolve; + // Apply resolve: CLI takes precedence over config. Only use config when CLI did not specify. + var resolve = input.Resolve ?? config.Bundle.Resolve; return input with { diff --git a/src/tooling/docs-builder/Commands/ChangelogCommand.cs b/src/tooling/docs-builder/Commands/ChangelogCommand.cs index 0ab1934c3..7329b9651 100644 --- a/src/tooling/docs-builder/Commands/ChangelogCommand.cs +++ b/src/tooling/docs-builder/Commands/ChangelogCommand.cs @@ -600,7 +600,7 @@ public async Task Bundle( } } - // Determine resolve: CLI --no-resolve takes precedence, then CLI --resolve, then config default + // Determine resolve: CLI --no-resolve and --resolve override config. null = use config default. var shouldResolve = noResolve ? false : resolve; // Process each --hide-features occurrence: each can be comma-separated feature IDs or a file path @@ -632,7 +632,7 @@ public async Task Bundle( All = all, InputProducts = inputProducts, OutputProducts = outputProducts, - Resolve = shouldResolve ?? false, + Resolve = shouldResolve, Prs = allPrs.Count > 0 ? allPrs.ToArray() : null, Issues = allIssues.Count > 0 ? allIssues.ToArray() : null, Owner = owner, diff --git a/tests/Elastic.Changelog.Tests/Changelogs/BundleChangelogsTests.cs b/tests/Elastic.Changelog.Tests/Changelogs/BundleChangelogsTests.cs index bad804736..701091d6f 100644 --- a/tests/Elastic.Changelog.Tests/Changelogs/BundleChangelogsTests.cs +++ b/tests/Elastic.Changelog.Tests/Changelogs/BundleChangelogsTests.cs @@ -1435,6 +1435,62 @@ public async Task BundleChangelogs_WithResolve_CopiesChangelogContents() bundleContent.Should().Contain("description: This is a test feature"); } + [Fact] + public async Task BundleChangelogs_WithExplicitResolveFalse_OverridesConfigResolveTrue() + { + // Arrange - config has resolve: true, but CLI passes Resolve = false (--no-resolve). + // The explicit CLI value must win. + + // language=yaml + var configContent = + """ + bundle: + resolve: true + """; + + var configPath = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString(), "changelog.yml"); + FileSystem.Directory.CreateDirectory(FileSystem.Path.GetDirectoryName(configPath)!); + await FileSystem.File.WriteAllTextAsync(configPath, configContent, TestContext.Current.CancellationToken); + + // language=yaml + var changelog1 = + """ + title: Test feature + type: feature + products: + - product: elasticsearch + target: 9.2.0 + prs: + - https://github.com/elastic/elasticsearch/pull/100 + """; + + var file1 = FileSystem.Path.Combine(_changelogDir, "1755268130-test-feature.yaml"); + await FileSystem.File.WriteAllTextAsync(file1, changelog1, TestContext.Current.CancellationToken); + + var input = new BundleChangelogsArguments + { + Directory = _changelogDir, + All = true, + Resolve = false, + Config = configPath, + Output = FileSystem.Path.Combine(FileSystem.Path.GetTempPath(), Guid.NewGuid().ToString(), "bundle.yaml") + }; + + // Act + var result = await ServiceWithConfig.BundleChangelogs(Collector, input, TestContext.Current.CancellationToken); + + // Assert + result.Should().BeTrue(); + Collector.Errors.Should().Be(0); + + var bundleContent = await FileSystem.File.ReadAllTextAsync(input.Output, TestContext.Current.CancellationToken); + + // An unresolved bundle has only a file reference — no inline title/type fields + bundleContent.Should().Contain("name: 1755268130-test-feature.yaml"); + bundleContent.Should().NotContain("title: Test feature"); + bundleContent.Should().NotContain("type: feature"); + } + [Fact] public async Task BundleChangelogs_WithResolve_PreservesSpecialCharactersInUtf8() {