From ecd58fc0f27ad79386c3ca8b8dbf52c33227caeb Mon Sep 17 00:00:00 2001 From: Amy Blais <29708087+amyblais@users.noreply.github.com> Date: Mon, 11 May 2026 13:38:47 +0300 Subject: [PATCH 1/3] Update generate_changelog.py --- .github/scripts/generate_changelog.py | 52 ++++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index a8c213bc3d2..85902e7da7a 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -32,21 +32,32 @@ Here are your instructions: -1. **Section structure:** Use `###` for top-level sections and `####` for subsections. Only include sections that have relevant content — do not output empty sections. +1. **Section structure:** Use `###` for top-level sections and `####` for subsections. Only include sections that have relevant content — do not output empty sections. Do NOT add horizontal rules or line separators between sections. - Top-level sections and their subsections: + Top-level sections and their subsections, in this order: - `### Upgrade Impact` — for changes that affect upgrading, with subsections as applicable: - - `#### Database Schema Changes` — new tables, columns, indexes, or migrations - - `#### config.json` — new or changed configuration settings; group by plan (e.g. "Changes to Enterprise plans") - - `#### Compatibility` — browser, OS, or minimum version requirement changes - - `### Improvements` — for new features and enhancements. 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: - - `#### User Interface` — UI/UX changes, new visual features, pre-packaged plugin version updates - - `#### Administration` — System Console features, mmctl additions, logging, support packet changes + - `#### 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: + + 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: + - `#### User Interface` — UI/UX changes and new visual features. Pre-packaged plugin version updates go at the TOP of this subsection, before other items. + - `#### 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 + - `#### mmctl` — mmctl command additions or changes (use as a separate subsection when there are enough items to warrant it) - `#### Performance` — performance improvements - - `### Bug Fixes` — corrections to defects + - `### Bug Fixes` — corrections to defects. All items starting with "Fixed an issue..." go here, even if the raw note appeared in an Improvements context. - `### API Changes` — API additions, changes, or deprecations + - `### WebSocket Event Changes` — new or changed WebSocket events, if applicable - `### Audit Log Event Changes` — new or changed audit log events + - `### Open Source Components` — open source component additions or removals, if applicable - `### Go Version` — Go version updates - `### Security` — security-related fixes not already covered under Bug Fixes @@ -65,15 +76,15 @@ - File names (e.g., ``config.json``) - Feature flags (e.g., ``MM_FEATUREFLAGS_CJKSEARCH``) -4. **Markdown formatting:** Use `- ` bullet points for individual items within sections. Ensure correct and clean Markdown syntax throughout. +4. **Markdown formatting:** Use `- ` bullet points for individual items within sections. Ensure correct and clean Markdown syntax throughout. Do not insert horizontal rules (`---`) or any other separators between sections. 5. **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. Aim for clear, concise, and professional language. +6. **Proofreading:** Correct any typos, grammatical errors, awkward phrasing, or inconsistencies. Aim for clear, concise, and professional language. -8. **Tone:** Maintain a neutral, informative, and professional tone consistent with technical documentation. +7. **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.""" +8. **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: @@ -89,7 +100,7 @@ def get_milestone_number(repo: str, title: str) -> int | None: "sort": "due_on", # sort by due date "direction": "desc", # most recently due first, so active milestones are found quickly } - resp = requests.get(url, headers=HEADERS, params=params, timeout=30) + resp = requests.get(url, headers=HEADERS, params=params) resp.raise_for_status() milestones = resp.json() if not milestones: @@ -211,14 +222,21 @@ def polish_with_ai(raw_notes: list[str]) -> str: def prepend_to_changelog(entry: str, changelog_path: str = "CHANGELOG.md") -> None: - """Prepend a new version entry to the changelog file.""" + """Insert a new version entry into the changelog after the static file header block.""" existing = "" if os.path.exists(changelog_path): with open(changelog_path, "r") as f: existing = f.read() - # If the file starts with a top-level heading, insert the new entry below it - if existing.startswith("# "): + # The v11 changelog file has a static header block ending with the platform scope note. + # New entries are inserted immediately after this block so the header is preserved. + HEADER_END_MARKER = "may not represent all affected configurations.\n```" + + if HEADER_END_MARKER in existing: + idx = existing.index(HEADER_END_MARKER) + len(HEADER_END_MARKER) + new_content = existing[:idx] + "\n\n\n" + entry + existing[idx:] + elif existing.startswith("# "): + # Fallback: insert after the first top-level heading line parts = existing.split("\n", 2) new_content = parts[0] + "\n\n" + entry + ("\n" + parts[2] if len(parts) > 2 else "") else: From d4ac649b735cd48075ab46675e4ab878f2ab4706 Mon Sep 17 00:00:00 2001 From: Amy Blais <29708087+amyblais@users.noreply.github.com> Date: Mon, 11 May 2026 13:48:25 +0300 Subject: [PATCH 2/3] Update generate_changelog.py --- .github/scripts/generate_changelog.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index 85902e7da7a..865cca71014 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -28,6 +28,10 @@ "Accept": "application/vnd.github.v3+json", } +# Shared timeout for all GitHub API requests (seconds). +# Prevents the workflow from hanging indefinitely if the API stalls. +API_TIMEOUT = 30 + SYSTEM_PROMPT = """You are an expert technical writer and copyeditor for Mattermost software release notes. Your task is to transform raw, unstructured release notes from pull requests into a clean, categorized, and grammatically correct changelog entry that matches Mattermost's established changelog format exactly. Here are your instructions: @@ -57,8 +61,8 @@ - `### API Changes` — API additions, changes, or deprecations - `### WebSocket Event Changes` — new or changed WebSocket events, if applicable - `### Audit Log Event Changes` — new or changed audit log events - - `### Open Source Components` — open source component additions or removals, if applicable - `### Go Version` — Go version updates + - `### Open Source Components` — open source component additions or removals, if applicable - `### Security` — security-related fixes not already covered under Bug Fixes 2. **Sentence patterns:** Follow these conventions consistently: @@ -100,7 +104,7 @@ def get_milestone_number(repo: str, title: str) -> int | None: "sort": "due_on", # sort by due date "direction": "desc", # most recently due first, so active milestones are found quickly } - resp = requests.get(url, headers=HEADERS, params=params) + resp = requests.get(url, headers=HEADERS, params=params, timeout=API_TIMEOUT) resp.raise_for_status() milestones = resp.json() if not milestones: @@ -129,7 +133,7 @@ def get_merged_prs(repo: str, milestone_number: int) -> list: "per_page": 100, "page": page, } - resp = requests.get(url, headers=HEADERS, params=params) + resp = requests.get(url, headers=HEADERS, params=params, timeout=API_TIMEOUT) resp.raise_for_status() items = resp.json() if not items: @@ -221,7 +225,7 @@ def polish_with_ai(raw_notes: list[str]) -> str: return response.content[0].text.strip() -def prepend_to_changelog(entry: str, changelog_path: str = "CHANGELOG.md") -> 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 = "" if os.path.exists(changelog_path): @@ -302,7 +306,7 @@ def main(): entry += "_No release notes for this version._\n" changelog_path = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md") - prepend_to_changelog(entry, changelog_path) + insert_changelog_entry(entry, changelog_path) prs_with_notes = total_prs - len(no_notes_prs) print(f"✅ CHANGELOG.md updated with notes from {prs_with_notes} PR(s) across {len(REPOS)} repo(s)") From 42c6d682b5646c9d379a27188af4ce8a564548f3 Mon Sep 17 00:00:00 2001 From: Amy Blais <29708087+amyblais@users.noreply.github.com> Date: Mon, 11 May 2026 13:56:53 +0300 Subject: [PATCH 3/3] Update generate_changelog.py --- .github/scripts/generate_changelog.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py index 865cca71014..40a3e03652b 100644 --- a/.github/scripts/generate_changelog.py +++ b/.github/scripts/generate_changelog.py @@ -6,8 +6,11 @@ GITHUB_TOKEN - GitHub personal access token or Actions token REPOS - Comma-separated list of repositories in "owner/repo" format (e.g. "mattermost/mattermost,mattermost/enterprise") - MILESTONE - Milestone title (e.g. "v10.6") - VERSION - Version label for the changelog entry (e.g. "10.6") + MILESTONE - Milestone title (e.g. "v11.7.0") + VERSION - Version label for the changelog entry (e.g. "v11.7.0") + RELEASE_DATE - (optional) Release day date (e.g. "2026-05-15"). Defaults to today. + BLOG_POST_URL - (optional) Blog post URL for the Improvements section. + Auto-constructed from VERSION if not provided. ANTHROPIC_API_KEY - (optional) If set, release notes are polished by Claude before being written to the changelog. """ @@ -146,7 +149,6 @@ def get_merged_prs(repo: str, milestone_number: int) -> list: return prs - def extract_release_notes(body: str) -> list[str] | None: """ Extract release note text from a PR body. @@ -309,7 +311,7 @@ def main(): insert_changelog_entry(entry, changelog_path) prs_with_notes = total_prs - len(no_notes_prs) - print(f"✅ CHANGELOG.md updated with notes from {prs_with_notes} PR(s) across {len(REPOS)} repo(s)") + print(f"✅ Changelog updated with notes from {prs_with_notes} PR(s) across {len(REPOS)} repo(s)") if no_notes_prs: print(f"\n⚠️ {len(no_notes_prs)} PR(s) had no release notes (marked NONE or section missing):")