Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/changelog.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ bundle:
# This release includes new features and bug fixes.
#
# For more information, see the [release notes](https://www.elastic.co/docs/release-notes/product#product-{version}).
# Whether to show release dates in rendered changelog output (default: false)
# When true, release-date fields are displayed as "Released: date" text
# show_release_dates: false
# changelog-init-bundle-seed
# PR/issue link allowlist: when set (including []), only links to these owner/repo pairs are kept
# in bundle output; others are rewritten to '# PRIVATE:' sentinels (requires resolve: true).
Expand Down Expand Up @@ -255,6 +258,8 @@ bundle:
# # description: |
# # Elasticsearch {version} includes:
# # - Performance improvements
# # Optional: profile-specific release date display setting (overrides bundle.show_release_dates)
# # show_release_dates: false
# # - Bug fixes and stability enhancements
# #
# # Download the release binaries: https://github.com/{owner}/{repo}/releases/tag/v{version}
Expand Down
12 changes: 12 additions & 0 deletions docs/cli/changelog/bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ The `--input-products` option determines which changelog files are gathered for
: This value replaces information that would otherwise be derived from changelogs.
: When `rules.bundle.products` per-product overrides are configured, `--output-products` also supplies the product IDs used to choose the **rule context product** (first alphabetically) for Mode 3. To use a different product's rules, run a separate bundle with only that product in `--output-products`. For details, refer to [Single-product rule resolution algorithm](/contribute/changelog.md#changelog-bundle-rule-resolution).

`--no-release-date`
: Optional: Skip auto-population of release date in the bundle.
: By default, bundles are created with a `release-date` field set to today's date (UTC) or the GitHub release published date when using `--release-version`.
: Mutually exclusive with `--release-date`.
: **Not available in profile mode** — use bundle configuration instead.

`--release-date <string?>`
: Optional: Explicit release date for the bundle in YYYY-MM-DD format.
: Overrides the default auto-population behavior (today's date or GitHub release published date).
: Mutually exclusive with `--no-release-date`.
: **Not available in profile mode** — use bundle configuration instead.

`--owner <string?>`
: Optional: The GitHub repository owner, required when pull requests or issues are specified as numbers.
: Precedence: `--owner` flag > `bundle.owner` in `changelog.yml` > `elastic`.
Expand Down
5 changes: 5 additions & 0 deletions docs/cli/changelog/gh-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ docs-builder changelog gh-release <repo> [version] [options...] [-h|--help]
`--output <string?>`
: Optional: Output directory for the generated changelog files. Falls back to `bundle.directory` in `changelog.yml` when not specified. Defaults to `./changelogs`.

`--release-date <string?>`
: Optional: Explicit release date for the bundle in YYYY-MM-DD format.
: By default, the bundle uses the GitHub release's published date. This option overrides that behavior.
: If the GitHub release has no published date, falls back to today's date (UTC).

`--strip-title-prefix`
: Optional: Remove square brackets and the text within them from the beginning of pull request titles, and also remove a colon if it follows the closing bracket.
: For example, `"[Inference API] New embedding model support"` becomes `"New embedding model support"`.
Expand Down
22 changes: 22 additions & 0 deletions docs/contribute/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,28 @@ You can add introductory text to bundles using the `description` field. This tex
- `bundle.description`: Default description for all profiles
- `bundle.profiles.<name>.description`: Profile-specific description (overrides the default)

#### Release date control

You can control whether release dates appear in rendered changelog output using the `show_release_dates` field.

**Configuration locations:**

- `bundle.show_release_dates`: Default setting for all profiles (defaults to `false`)
- `bundle.profiles.<name>.show_release_dates`: Profile-specific setting (overrides the default)

**Behavior:**

- `false` (default): Release date fields are ignored during rendering, even if present in bundles
- `true`: Release dates are displayed when present (e.g., `_Released: April 9, 2026_`)

**Auto-population:**

Release dates are automatically added to bundles when using `changelog bundle` or `changelog gh-release`:

- **Default**: Uses today's date (UTC) when bundling
- **GitHub releases**: Uses the GitHub release's published date when available
- **Override**: Use `--release-date YYYY-MM-DD` or `--no-release-date` in option mode

**Placeholder support:**

Bundle descriptions support these placeholders:
Expand Down
2 changes: 2 additions & 0 deletions docs/syntax/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ Download the release binaries: https://github.com/elastic/elasticsearch/releases

When present, the `release-date` field is rendered immediately after the version heading as italicized text (e.g., `_Released: 2026-04-09_`). This is purely informative for end-users and is especially useful for components released outside the usual stack lifecycle, such as APM agents and EDOT agents.

The `show_release_dates` setting in `changelog.yml` (boolean, defaults to `false`) controls whether release dates appear in the rendered output. When set to `false`, the `release-date` field is ignored during rendering.

Bundle descriptions are rendered when present in the bundle YAML file. The description appears after the release date (if any) but before any entry sections. Descriptions support Markdown formatting including links, lists, and multiple paragraphs.

### Section types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ public record BundleConfiguration
/// </summary>
public IReadOnlyList<string>? LinkAllowRepos { get; init; }

/// <summary>
/// Whether to show release dates in rendered changelog output.
/// When true, bundles with release-date fields will display "Released: date" text.
/// Defaults to false.
/// </summary>
public bool ShowReleaseDates { get; init; }

/// <summary>
/// Named bundle profiles for different release scenarios.
/// </summary>
Expand Down Expand Up @@ -111,6 +118,13 @@ public record BundleProfile
/// </summary>
public IReadOnlyList<string>? HideFeatures { get; init; }

/// <summary>
/// Whether to show release dates in rendered changelog output for this profile.
/// When provided, overrides the bundle.show_release_dates default.
/// When true, bundles with release-date fields will display "Released: date" text.
/// </summary>
public bool? ShowReleaseDates { get; init; }

/// <summary>
/// Profile source type. When set to <c>"github_release"</c>, the profile fetches
/// PR references directly from a GitHub release and uses them as the bundle filter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public sealed record BundleDto
[YamlMember(Alias = "release-date", ApplyNamingConventions = false)]
public string? ReleaseDate { get; set; }
/// <summary>
/// Whether to show release dates in rendered changelog output for this bundle.
/// When true, the ReleaseDate field (if present) will be displayed as "Released: date" text.
/// </summary>
[YamlMember(Alias = "show-release-dates", ApplyNamingConventions = false)]
public bool? ShowReleaseDates { get; set; }
/// <summary>
/// Feature IDs that should be hidden when rendering this bundle.
/// Entries with matching feature-id values will be commented out in the output.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,17 @@ private static LoadedBundle MergeBundleGroup(IGrouping<string, LoadedBundle> gro
_ => releaseDates[0]
};

var mergedData = first.Data with { Description = mergedDescription, ReleaseDate = mergedReleaseDate };
var showReleaseDatesValues = bundlesList
.Select(b => b.Data?.ShowReleaseDates ?? false)
.Distinct()
.ToList();

// If all bundles agree on ShowReleaseDates, use that value; otherwise default to first bundle's value
var mergedShowReleaseDates = showReleaseDatesValues.Count == 1 ? showReleaseDatesValues[0] : first.Data?.ShowReleaseDates ?? false;

var mergedData = first.Data != null
? first.Data with { Description = mergedDescription, ReleaseDate = mergedReleaseDate, ShowReleaseDates = mergedShowReleaseDates }
: new Bundle { Description = mergedDescription, ReleaseDate = mergedReleaseDate, ShowReleaseDates = mergedShowReleaseDates };

return new LoadedBundle(
first.Version,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public static string SerializeBundle(Bundle bundle)
Products = dto.Products?.Select(ToBundledProduct).ToList() ?? [],
Description = dto.Description,
ReleaseDate = ParseReleaseDate(dto.ReleaseDate),
ShowReleaseDates = dto.ShowReleaseDates ?? false,
HideFeatures = dto.HideFeatures ?? [],
Entries = dto.Entries?.Select(ToBundledEntry).ToList() ?? []
};
Expand Down Expand Up @@ -249,6 +250,7 @@ private static ChangelogEntryType ParseEntryType(string? value)
Products = bundle.Products.Select(ToDto).ToList(),
Description = bundle.Description,
ReleaseDate = bundle.ReleaseDate?.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
ShowReleaseDates = bundle.ShowReleaseDates ? bundle.ShowReleaseDates : null,
HideFeatures = bundle.HideFeatures.Count > 0 ? bundle.HideFeatures.ToList() : null,
Entries = bundle.Entries.Select(ToDto).ToList()
};
Expand Down
6 changes: 6 additions & 0 deletions src/Elastic.Documentation/ReleaseNotes/Bundle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ public record Bundle
/// </summary>
public DateOnly? ReleaseDate { get; init; }

/// <summary>
/// Whether to show release dates in rendered changelog output for this bundle.
/// When true, the ReleaseDate field (if present) will be displayed as "Released: date" text.
/// </summary>
public bool ShowReleaseDates { get; init; }

/// <summary>
/// Feature IDs that should be hidden when rendering this bundle.
/// Entries with matching feature-id values will be commented out in the output.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private static string RenderSingleBundle(
};

var displayVersion = VersionOrDate.FormatDisplayVersion(bundle.Version);
return GenerateMarkdown(displayVersion, titleSlug, bundle.Repo, bundle.Owner, entriesByType, subsections, hideLinks, typeFilter, publishBlocker, bundle.Data?.Description, bundle.Data?.ReleaseDate);
return GenerateMarkdown(displayVersion, titleSlug, bundle.Repo, bundle.Owner, entriesByType, subsections, hideLinks, typeFilter, publishBlocker, bundle.Data?.Description, bundle.Data?.ReleaseDate, bundle.Data?.ShowReleaseDates ?? false);
}

/// <summary>
Expand Down Expand Up @@ -154,7 +154,8 @@ private static string GenerateMarkdown(
ChangelogTypeFilter typeFilter,
PublishBlocker? publishBlocker,
string? description = null,
DateOnly? releaseDate = null)
DateOnly? releaseDate = null,
bool showReleaseDates = false)
{
var sb = new StringBuilder();

Expand All @@ -178,8 +179,8 @@ private static string GenerateMarkdown(

_ = sb.AppendLine(CultureInfo.InvariantCulture, $"## {title}");

// Add release date if present
if (releaseDate is { } date)
// Add release date if present and ShowReleaseDates is enabled
if (showReleaseDates && releaseDate is { } date)
{
_ = sb.AppendLine();
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"_Released: {date.ToString("MMMM d, yyyy", CultureInfo.InvariantCulture)}_");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ public record BundleChangelogsArguments
/// </summary>
public string? Description { get; init; }

/// <summary>
/// Optional explicit release date for the bundle in YYYY-MM-DD format.
/// When provided, overrides auto-population behavior.
/// </summary>
public string? ReleaseDate { get; init; }

/// <summary>
/// Whether to show release dates in rendered output. When null, inherits from config/profile.
/// </summary>
public bool? ShowReleaseDates { get; init; }

/// <summary>
/// When non-null (including empty), PR/issue links are filtered to this <c>owner/repo</c> allowlist (from changelog.yml <c>bundle.link_allow_repos</c>).
/// </summary>
Expand Down Expand Up @@ -354,6 +365,36 @@ public async Task<bool> BundleChangelogs(IDiagnosticsCollector collector, Bundle
}
}

// Apply release date auto-population and ShowReleaseDates setting
var finalReleaseDate = bundleData.ReleaseDate; // Preserve existing date if present
if (!string.IsNullOrEmpty(input.ReleaseDate))
{
// Explicit CLI override
if (DateOnly.TryParseExact(input.ReleaseDate, "yyyy-MM-dd", out var parsedDate))
{
finalReleaseDate = parsedDate;
}
else
{
collector.EmitError(string.Empty, $"Invalid release date format '{input.ReleaseDate}'. Expected YYYY-MM-DD format.");
return false;
}
}
else if (finalReleaseDate == null)
{
// Auto-populate with today's date (UTC) if no existing date
finalReleaseDate = DateOnly.FromDateTime(DateTime.UtcNow);
}

// Apply ShowReleaseDates setting (input takes precedence over config)
var showReleaseDates = input.ShowReleaseDates ?? config?.Bundle?.ShowReleaseDates ?? false;

bundleData = bundleData with
{
ReleaseDate = finalReleaseDate,
ShowReleaseDates = showReleaseDates
};

// Write bundle file
await WriteBundleFileAsync(bundleData, outputPath, ctx);

Expand Down Expand Up @@ -395,6 +436,7 @@ public async Task<bool> BundleChangelogs(IDiagnosticsCollector collector, Bundle
string? owner = null;
string[]? mergedHideFeatures = null;
string? profileDescription = null;
bool? profileShowReleaseDates = null;

if (config?.Bundle?.Profiles != null && config.Bundle.Profiles.TryGetValue(input.Profile!, out var profile))
{
Expand Down Expand Up @@ -437,6 +479,9 @@ public async Task<bool> BundleChangelogs(IDiagnosticsCollector collector, Bundle
owner = profile.Owner ?? config.Bundle.Owner;
mergedHideFeatures = profile.HideFeatures?.Count > 0 ? [.. profile.HideFeatures] : null;

// Profile-level ShowReleaseDates takes precedence; fall back to bundle-level default
profileShowReleaseDates = profile.ShowReleaseDates ?? config.Bundle.ShowReleaseDates;

// Handle profile-specific description with placeholder substitution
var descriptionTemplate = profile.Description ?? config.Bundle.Description;
if (!string.IsNullOrEmpty(descriptionTemplate))
Expand Down Expand Up @@ -480,7 +525,8 @@ public async Task<bool> BundleChangelogs(IDiagnosticsCollector collector, Bundle
Repo = repo,
Owner = owner,
HideFeatures = mergedHideFeatures,
Description = profileDescription
Description = profileDescription,
ShowReleaseDates = profileShowReleaseDates ?? config?.Bundle?.ShowReleaseDates
};
}

Expand All @@ -507,6 +553,9 @@ private BundleChangelogsArguments ApplyConfigDefaults(BundleChangelogsArguments
// Apply description: CLI takes precedence; fall back to bundle-level config default
var description = input.Description ?? config.Bundle.Description;

// Apply ShowReleaseDates: CLI takes precedence; fall back to bundle-level config default
var showReleaseDates = input.ShowReleaseDates ?? config.Bundle.ShowReleaseDates;

return input with
{
Directory = directory,
Expand All @@ -515,6 +564,7 @@ private BundleChangelogsArguments ApplyConfigDefaults(BundleChangelogsArguments
Repo = repo,
Owner = owner,
Description = description,
ShowReleaseDates = showReleaseDates,
LinkAllowRepos = config.Bundle.LinkAllowRepos
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ private static PivotConfiguration ConvertPivot(PivotConfigurationYaml yamlPivot)
Repo = kvp.Value.Repo,
Owner = kvp.Value.Owner,
HideFeatures = kvp.Value.HideFeatures?.Values,
ShowReleaseDates = kvp.Value.ShowReleaseDates,
Source = kvp.Value.Source
});
}
Expand All @@ -534,6 +535,7 @@ private static PivotConfiguration ConvertPivot(PivotConfigurationYaml yamlPivot)
Repo = yaml.Repo,
Owner = yaml.Owner,
LinkAllowRepos = linkAllowRepos,
ShowReleaseDates = yaml.ShowReleaseDates ?? false,
Profiles = profiles
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ static GitHubReleaseService()
Body = releaseData.Body ?? string.Empty,
Prerelease = releaseData.Prerelease,
Draft = releaseData.Draft,
HtmlUrl = releaseData.HtmlUrl ?? string.Empty
HtmlUrl = releaseData.HtmlUrl ?? string.Empty,
PublishedAt = releaseData.PublishedAt
};
}

Expand All @@ -126,6 +127,9 @@ private sealed class GitHubReleaseResponse

[JsonPropertyName("html_url")]
public string? HtmlUrl { get; set; }

[JsonPropertyName("published_at")]
public DateTimeOffset? PublishedAt { get; set; }
}

[JsonSerializable(typeof(GitHubReleaseResponse))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public record GitHubReleaseInfo
/// The URL to the release page on GitHub
/// </summary>
public string HtmlUrl { get; init; } = "";

/// <summary>
/// The date and time when this release was published on GitHub
/// </summary>
public DateTimeOffset? PublishedAt { get; init; }
}

/// <summary>
Expand Down
Loading
Loading