Skip to content
Merged
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
46 changes: 46 additions & 0 deletions changelog/fragments/1759172049-refine-markdown-output.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# REQUIRED
# Kind can be one of:
# - breaking-change: a change to previously-documented behavior
# - deprecation: functionality that is being removed in a later release
# - bug-fix: fixes a problem in a previous version
# - enhancement: extends functionality but does not break or fix existing behavior
# - feature: new functionality
# - known-issue: problems that we are aware of in a given version
# - security: impacts on the security of a product or a user’s deployment.
# - upgrade: important information for someone upgrading from a prior version
# - other: does not fit into any of the other categories
kind: enhancement

# REQUIRED for all kinds
# Change summary; a 80ish characters long description of the change.
summary: Add logic that will add `include`s to `_snippets` files in the correct order if the `rendered_changelog_destination` in the `config.changelog.yaml` is using `release-notes/_snippets`.

# REQUIRED for breaking-change, deprecation, known-issue
# Long description; in case the summary is not enough to describe the change
# this field accommodate a description without length limits.
description: When using the `release-notes/_snippets` model, there are multiple levels of nesting to make it as easy as possible to insert new patch version sections in the correct order. The updates in this PR will (1) eliminate any manual steps in the resulting release notes PR outside of copy edits, and (2) reduce the chance of conflicts when forward porting to later minors and `main`.

# REQUIRED for breaking-change, deprecation, known-issue
# impact:

# REQUIRED for breaking-change, deprecation, known-issue
# action:

# REQUIRED for all kinds
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
component:

# AUTOMATED
# OPTIONAL to manually add other PR URLs
# PR URL: A link the PR that added the changeset.
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
# Please provide it if you are adding a fragment for a different PR.
pr:
- https://github.com/elastic/elastic-agent-changelog-tool/pull/220

# AUTOMATED
# OPTIONAL to manually add other issue URLs
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
# If not present is automatically filled by the tooling with the issue linked to the PR number.
# issue: https://github.com/owner/repo/1234
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ $ elastic-agent-changelog-tool render --version 0.1.0 --file_type markdown
This will create three files:

* `./changelog/0.1.0/index.md`
* `./changelog/0.1.0/breaking.md`
* `./changelog/0.1.0/breaking-changes.md`
* `./changelog/0.1.0/deprecations.md`

### AsciiDoc
Expand Down
25 changes: 16 additions & 9 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ The side effect is that the changelog will include all entries from latest stabl
Depending on the specified `file_type`, this will generate the following files:
* `markdown`:
* Release notes: `./changelog/<version>/index.md`
* Breaking changes: `./changelog/<version>/breaking.md`
* Breaking changes: `./changelog/<version>/breaking-changes.md`
* Deprecations: `./changelog/<version>/deprecations.md`
* `asciidoc`: `changelog/<version>.asciidoc`
3. Use the rendered changelog.
Expand Down Expand Up @@ -133,19 +133,26 @@ These steps require [GitHub Authentication](./github-authentication.md).
```
$ elastic-agent-changelog-tool cleanup
```
1. Commit the previous changes (consolidated changelod and removed files)
1. Commit the previous changes (consolidated changelog and removed files)
1. From the root folder of the repository run:
```
$ elastic-agent-changelog-tool render --version x.y.z --file_type <asciidoc|markdown>
```

Depending on the specified `file_type`, this will generate the following files:
* `markdown`:
* Release notes: `./changelog/<version>/index.md`
* Breaking changes: `./changelog/<version>/breaking.md`
* Deprecations: `./changelog/<version>/deprecations.md`
* `asciidoc`: `changelog/<version>.asciidoc`
1. Integrate the generated fragment into the changelog. If the changelog is stored in the same repository, commit the changes in this same branch.
>IMPORTANT: Use `file_type` `markdown` for 9.x versions and `asciidoc` for 8.x versions.

The files that are generated depend on the specified `file_type`. The destination directory depends on the `rendered_changelog_destination` defined in the the repo's `config.changelog.yaml`. If no `rendered_changelog_destination` is specified, it will be added to the `changelog` directory.

* `markdown`: These files will be created:
* Release notes: `<rendered_changelog_destination>/<version>/index.md`
* Breaking changes: `<rendered_changelog_destination>/<version>/breaking-changes.md`
* Deprecations: `<rendered_changelog_destination>/<version>/deprecations.md`

If the `rendered_changelog_destination` is set to `release-notes/_snippets`, the related `_snippets` files will automatically be updated.

* `asciidoc`: There will be one file created, `<rendered_changelog_destination>/<version>.asciidoc`, and you will need to integrate the generated content into the changelog.

1. If the changelog is stored in the same repository, commit the changes in this same branch.
1. Create a PR with the changes to the `x.y` branch.


Expand Down
84 changes: 72 additions & 12 deletions internal/changelog/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (r Renderer) Render() error {
if template == "markdown-index" {
return path.Join(r.dest, r.changelog.Version, "index.md")
} else if template == "markdown-breaking" {
return path.Join(r.dest, r.changelog.Version, "breaking.md")
return path.Join(r.dest, r.changelog.Version, "breaking-changes.md")
} else if template == "markdown-deprecations" {
return path.Join(r.dest, r.changelog.Version, "deprecations.md")
} else {
Expand All @@ -208,19 +208,35 @@ func (r Renderer) Template() ([]byte, error) {
var err error

if embeddedFileName, ok := assets.GetEmbeddedTemplates()[r.templ]; ok {
if r.templ == "markdown-index" {
data, err = assets.MarkdownIndexTemplate.ReadFile(embeddedFileName)
} else if r.templ == "markdown-breaking" {
data, err = assets.MarkdownBreakingTemplate.ReadFile(embeddedFileName)
} else if r.templ == "markdown-deprecations" {
data, err = assets.MarkdownDeprecationsTemplate.ReadFile(embeddedFileName)
} else if r.templ == "asciidoc-embedded" {
data, err = assets.AsciidocTemplate.ReadFile(embeddedFileName)
var readFunc func(string) ([]byte, error)
switch r.templ {
case "markdown-index":
readFunc = assets.MarkdownIndexTemplate.ReadFile
case "markdown-breaking":
readFunc = assets.MarkdownBreakingTemplate.ReadFile
case "markdown-deprecations":
readFunc = assets.MarkdownDeprecationsTemplate.ReadFile
case "asciidoc-embedded":
readFunc = assets.AsciidocTemplate.ReadFile
}
if err != nil {
return []byte{}, fmt.Errorf("cannot read embedded template: %s %w", embeddedFileName, err)
if readFunc != nil {
data, err = readFunc(embeddedFileName)
if err != nil {
return nil, fmt.Errorf("cannot read embedded template: %s %w", embeddedFileName, err)
}
// If using the snippet/include model, update the includes
if strings.Contains(r.dest, "release-notes/_snippets") {
switch r.templ {
case "markdown-index":
addInclude(r.fs, r.changelog.Version, r.dest, "index")
case "markdown-breaking":
addInclude(r.fs, r.changelog.Version, r.dest, "breaking-changes")
case "markdown-deprecations":
addInclude(r.fs, r.changelog.Version, r.dest, "deprecations")
}
}
return data, nil
}
return data, nil
}

data, err = afero.ReadFile(r.fs, r.templ)
Expand Down Expand Up @@ -321,3 +337,47 @@ func buildTitleByComponents(entries []Entry) string {
return match
}
}

func addInclude(fs afero.Fs, version, dest, templ string) {
// Extract minor version (e.g., "8.12" from "8.12.1")
minorVersion := regexp.MustCompile(`^\d+\.\d+`).FindString(version)
if minorVersion == "" {
fmt.Printf("Could not get minor version from: %v\n", version)
return
}

// Extract include directory (e.g., "/release-notes/...")
includeDir := regexp.MustCompile(`/release-notes/.+$`).FindString(dest)
if includeDir == "" {
fmt.Printf("Could not derive include directory from: %v\n", dest)
return
}

minorFilePath := fmt.Sprintf("%s/%s/%s.md", dest, templ, minorVersion)
templateTypeFilePath := fmt.Sprintf("%s/%s.md", dest, templ)

// Read or create the minor file
minorFileContent, err := afero.ReadFile(fs, minorFilePath)
if err != nil {
// Create the file
if err := afero.WriteFile(fs, minorFilePath, nil, changelogFilePerm); err == nil {
fmt.Printf("Created new empty snippet file: %s\n", minorFilePath)
}
// Prepend new minor version include to the template type file (e.g. "breaking-changes")
if templateTypeFileContent, err := afero.ReadFile(fs, templateTypeFilePath); err == nil {
newMinorInclude := fmt.Sprintf(":::{include} %s/%s/%s.md\n:::", includeDir, templ, minorVersion)
newContent := fmt.Sprintf("%s\n\n%s", newMinorInclude, templateTypeFileContent)
if err := afero.WriteFile(fs, templateTypeFilePath, []byte(newContent), changelogFilePerm); err == nil {
fmt.Printf("Updated snippet file: %s\n", templateTypeFilePath)
}
}
minorFileContent = nil // ensure it's empty for next step
}

// Prepend new patch version include to the minor file
newPatchInclude := fmt.Sprintf(":::{include} %s/%s/%s.md\n:::", includeDir, version, templ)
newContent := fmt.Sprintf("%s\n\n%s", newPatchInclude, minorFileContent)
if err := afero.WriteFile(fs, minorFilePath, []byte(newContent), changelogFilePerm); err == nil {
fmt.Printf("Updated snippet file: %s\n", minorFilePath)
}
}
Loading