Skip to content
Open
Changes from 2 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
69 changes: 55 additions & 14 deletions .github/scripts/generate_changelog.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#!/usr/bin/env python3
"""
Generate a changelog from merged PR release notes for a given milestone.
Expand Down Expand Up @@ -65,16 +66,15 @@

- `### Upgrade Impact` — for changes that affect upgrading, with subsections as applicable:
- `#### Database Schema Changes` — schema migrations such as new tables, new columns, changed columns, or new indexes. Example items: "Added a new ``Watermarks`` table.", "Added a new column ``DeleteAt`` to the ``ChannelMembers`` table."
- `#### config.json` — new or changed configuration settings. Use this exact block format for each plan grouping:
- `#### config.json` — new or changed configuration settings. Use this exact block format for each plan grouping (no blank line between the `#### config.json` heading and the description paragraph, and no blank line between the description paragraph and the first bullet):

New setting options were added to ``config.json``. Below is a list of the additions and their default values on install. The settings can be modified in ``config.json``, or the System Console when available.

- **Changes to Enterprise Advanced plan:**
- Under ``ExperimentalSettings`` in ``config.json``, added ``EnableWatermark`` configuration setting to add watermarking toggle in the server.

Adapt the plan name (e.g. "Changes to All plans:", "Changes to Enterprise plan:", "Changes to Enterprise Advanced plan:") and list each setting change as a bullet under the appropriate plan heading.
- `#### Compatibility` — minimum version requirement changes for browsers, OS, or clients. Example: "Updated minimum Edge and Chrome versions to 146+."
- `### Improvements` — for new features and enhancements only. Do NOT place items beginning with "Fixed..." here — those belong in Bug Fixes. Begin this section with the line `See [this blog post](BLOG_POST_URL) on the highlights in our latest release.` (use the exact placeholder `BLOG_POST_URL` — it will be replaced automatically). Then add subsections as applicable:
- `### Improvements` — for new features and enhancements only. Do NOT place items beginning with "Fixed..." here — those belong in Bug Fixes. Begin this section with the line `See [this blog post](BLOG_POST_URL) on the highlights in our latest release.` (use the exact placeholder `BLOG_POST_URL` — it will be replaced automatically), followed by a blank line before the first `####` subsection heading. Then add subsections as applicable:
- `#### User Interface` — user interface and UX changes and new visual features. Pre-packaged plugin version updates go at the TOP of this subsection, before other items. Always write "user interface" in full — never abbreviate as "UI".
- `#### Plugins/Integrations` — plugin and integration improvements (use as a separate subsection when there are enough items to warrant it)
- `#### Administration` — System Console features, logging, support packet changes
Expand All @@ -94,7 +94,7 @@
- Improvements to existing things: "Improved [thing]..." or "Updated [thing]..."
- Removals: "Removed [thing]..."

3. **Terminology:** Always write "user interface" in full — never use the abbreviation "UI".
3. **Terminology:** Always write "user interface" in full — never use the abbreviation "UI". Always spell out messaging abbreviations: "DM" → "Direct Message", "GM" → "Group Message", "DM/GM" → "Direct/Group Message".

4. **Code formatting:** Use double backticks for all of the following:
- Configuration settings (e.g., ``ServiceSettings.EnableDynamicClientRegistration``)
Expand All @@ -106,15 +106,17 @@
- Feature flags (e.g., ``MM_FEATUREFLAGS_CJKSEARCH``)
- Package names in Open Source Components (e.g., ``x/text``)

5. **Markdown formatting:** Indent each bullet point with two spaces (e.g., ` - item`). Ensure correct and clean Markdown syntax throughout. Do not insert horizontal rules (`---`) or any other separators between sections. Do not add a blank line between a heading and its first bullet.
5. **Markdown formatting:** Indent each bullet point with two spaces (e.g., ` - item`). Ensure correct and clean Markdown syntax throughout. Do not insert horizontal rules (`---`) or any other separators between sections. Do not add a blank line between a section/subsection heading and its first bullet point.

6. **License requirements:** When a feature requires a specific Mattermost license, note it inline at the end of the bullet point (e.g., "Requires Enterprise Advanced license" or "Requires Enterprise license").

7. **Proofreading:** Correct any typos, grammatical errors, awkward phrasing, or inconsistencies. Replace any instance of "Fixed a bug" with "Fixed an issue". Aim for clear, concise, and professional language.
7. **One section per release note:** Each raw release note must appear in exactly one section — whichever section best fits its primary purpose. Do not split a single release note across multiple sections or subsections, even if it touches more than one area (e.g. an admin feature that also introduces an API endpoint belongs entirely in Administration, not partially in Administration and partially in API Changes).

8. **Proofreading:** Correct any typos, grammatical errors, awkward phrasing, or inconsistencies. Replace any instance of "Fixed a bug" with "Fixed an issue". Aim for clear, concise, and professional language.

8. **Tone:** Maintain a neutral, informative, and professional tone consistent with technical documentation.
9. **Tone:** Maintain a neutral, informative, and professional tone consistent with technical documentation.

9. **Focus:** Output only the section content (headings and bullet points). Do not include the release version header line or any introductory or concluding remarks from yourself."""
10. **Focus:** Output only the section content (headings and bullet points). Do not include the release version header line or any introductory or concluding remarks from yourself."""


def get_milestone_number(repo: str, title: str) -> int | None:
Expand Down Expand Up @@ -264,6 +266,37 @@ def polish_with_ai(raw_notes: list[str]) -> str:
return response.content[0].text.strip()


def normalize_go_version(go_version: str) -> str:
"""Normalize a Go version string to v-prefixed form.

Accepts both "go1.22.5" (Go toolchain convention) and "v1.22.5"
(Mattermost changelog convention) and always returns the latter.
"""
v = go_version.strip()
if v.startswith("go"):
v = v[2:] # strip the "go" prefix
if not v.startswith("v"):
v = "v" + v
return v


def extract_previous_go_version(changelog_path: str) -> str | None:
"""Scan the changelog file for the most recently documented Go version.

Used when GO_VERSION is not supplied (i.e. unchanged from the previous
release) so the generated entry still shows a concrete version number
rather than a generic "unchanged" message.
"""
if not os.path.exists(changelog_path):
return None
with open(changelog_path, "r", encoding="utf-8") as f:
content = f.read()
match = re.search(r"is built with Go\s+``(v?[\d.]+)``", content)
if match:
return normalize_go_version(match.group(1))
return None


def insert_changelog_entry(entry: str, changelog_path: str = "CHANGELOG.md") -> None:
"""Insert a new version entry into the changelog after the static file header block."""
existing = ""
Expand Down Expand Up @@ -323,6 +356,7 @@ def main():
release_type = os.environ.get("RELEASE_TYPE", "feature").strip().lower()
release_date = os.environ.get("RELEASE_DATE", "").strip() or date.today().strftime("%Y-%m-%d")
go_version = os.environ.get("GO_VERSION", "").strip()
changelog_path = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")

if release_type == "esr":
anchor = f"(release-{version_short}-extended-support-release)="
Expand All @@ -332,16 +366,24 @@ def main():
f"(https://docs.mattermost.com/product-overview/release-policy.html#release-types)"
)
else:
anchor = f"(release-{version_short})="
heading = f"## Release {version_short}"
anchor = f"(release-{version_short}-feature-release)="
heading = (
f"## Release {version_short} - "
f"[Feature Release]"
f"(https://docs.mattermost.com/product-overview/release-policy.html#release-types)"
)
Comment thread
amyblais marked this conversation as resolved.

entry = f"{anchor}\n{heading}\n\n**Release day: {release_date}**\n\n"

# Build the Go Version section content
# Build the Go Version section content.
# Mattermost uses a bare sentence (not a bullet point) in this section.
# When GO_VERSION is not provided (unchanged from previous release), we look up
# the previous version from the changelog so the line still shows a concrete number.
if go_version:
go_section = f"### Go Version\n - Updated to ``{go_version}``."
formatted_go = normalize_go_version(go_version)
else:
go_section = "### Go Version\n - Go version is the same as in the previous release."
formatted_go = extract_previous_go_version(changelog_path) or "(see previous release)"
go_section = f"### Go Version\n - {version_short} is built with Go ``{formatted_go}``."

if all_notes:
polished = polish_with_ai(all_notes)
Expand Down Expand Up @@ -376,7 +418,6 @@ def main():
entry += go_section + "\n"
entry += "\n_No other release notes for this version._\n"

changelog_path = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md")
insert_changelog_entry(entry, changelog_path)

prs_with_notes = total_prs - len(no_notes_prs)
Expand Down
Loading