Skip to content

docs: generate the site changelog in a site-friendly format#5740

Open
taliesin-ai wants to merge 2 commits into
masterfrom
docs/changelog-site-format
Open

docs: generate the site changelog in a site-friendly format#5740
taliesin-ai wants to merge 2 commits into
masterfrom
docs/changelog-site-format

Conversation

@taliesin-ai

@taliesin-ai taliesin-ai commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Description

The changelog page at https://v3.wails.io/changelog/ was a verbatim copy of the working changelog file: it opened with a legend, raw /* */ comment blocks rendered as literal text and an empty [Unreleased] heading, and the Keep-a-Changelog category headers (## Added, ## Fixed, ...) sat at the same heading level as the version headers, so the page's table of contents could not nest them.

This extends the UNRELEASED_CHANGELOG processing so the site file is generated in a format that works on the site:

  • The page now starts with the latest version header (e.g. v3.0.0-alpha2.112 - 2026-07-03). The do-not-edit notice moved into an invisible MDX comment, and new releases are inserted below a hidden CHANGELOG-INSERT-MARKER comment instead of a visible ## [Unreleased] heading. The emoji legend is now a single sentence instead of a bullet list.
  • The ToC nests properly: release.go demotes category headers to ### when moving UNRELEASED_CHANGELOG.md content into the page, so Starlight nests Added/Changed/Fixed under each version. The entire released history (English + Indonesian pages) is migrated to the same format.
  • validate-changelog.go now moves entries that PRs add directly to the generated page into v3/UNRELEASED_CHANGELOG.md (there is no [Unreleased] section on the page any more). changelog-v3.yml commits both files and its PR comment explains the new flow, showing the corrected UNRELEASED_CHANGELOG.md instead of pasting the whole 1600-line changelog.
  • GitHub release notes are unchanged — still built from the raw UNRELEASED_CHANGELOG.md content.

Contributor flow is unchanged: entries go in v3/UNRELEASED_CHANGELOG.md (or are auto-added on merge), and the nightly release moves them to the site page.

Verification

  • go test ./v3/tasks/release/ passes, including a new test that runs applyChangelogUpdates end-to-end against the marker format and a formatChangelogForSite unit test.
  • Validator exercised end-to-end: simulated a PR adding entries under a released version's ### Added/### Fixed; both were removed from changelog.mdx and inserted into the matching ## Added/## Fixed sections of UNRELEASED_CHANGELOG.md (VALIDATION_RESULT=fixed).
  • Full astro build of the docs passes. The built page has no rendered comment junk, the first content heading is v3.0.0-alpha2.112 - 2026-07-03, and the ToC nests Added/Changed/Fixed at depth 1 under each version at depth 0 (verified in the generated HTML for both /changelog/ and /id/changelog/).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Changelog pages are now generated in a cleaner, site-friendly format with version sections first and nested category headings.
    • Added clearer guidance for managing upcoming v3 release notes.
  • Bug Fixes

    • Improved handling of misplaced changelog entries so they’re moved into the correct upcoming release section.
    • Fixed changelog formatting across recent and older releases, including heading structure and a missing release note entry.

The docs changelog page was a verbatim copy of the working changelog:
it opened with a legend, raw /* */ comment blocks rendered as text and
an empty [Unreleased] heading, and the Keep-a-Changelog category
headers (## Added, ## Fixed, ...) sat at the same level as the version
headers, so the page table of contents could not nest them.

The release processing now generates the page in a format that works
on the site:

- changelog.mdx starts with the latest version header; the do-not-edit
  notice moved into an invisible MDX comment and new releases are
  inserted below a hidden marker comment instead of '## [Unreleased]'
- release.go demotes category headers to '###' when moving
  UNRELEASED_CHANGELOG.md content into the page, so Starlight nests
  Added/Changed/Fixed under each version in the table of contents
- the whole released history (en + id) is migrated to the same format
- validate-changelog.go now moves entries that PRs add directly to the
  generated page into v3/UNRELEASED_CHANGELOG.md (there is no
  [Unreleased] section on the page any more), and changelog-v3.yml
  commits both files and explains the new flow in its PR comment

GitHub release notes are unchanged: they are built from the raw
UNRELEASED_CHANGELOG.md content as before.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changelog handling for v3 is reworked to route unreleased entries through v3/UNRELEASED_CHANGELOG.md instead of editing the generated changelog.mdx directly. The validator script, release automation, CI workflow, contributor docs, and generated changelog files (English and Indonesian) are updated accordingly, including a new insertion marker and heading normalization for site formatting.

Changes

Changelog pipeline rework

Layer / File(s) Summary
Validator CLI and fix logic
v3/scripts/validate-changelog.go
Accepts an optional unreleased changelog path argument, reworks section/category parsing, adds category normalization, and rewrites the fix logic to move misplaced entries into UNRELEASED_CHANGELOG.md.
Release task changelog insertion
v3/tasks/release/release.go, v3/tasks/release/release_test.go
Adds changelogInsertMarker and formatChangelogForSite, changes applyChangelogUpdates to split on the marker and demote category headings, with tests covering the new insertion path.
CI workflow and contributor docs updates
.github/workflows/changelog-v3.yml, CONTRIBUTING.md, v3/UNRELEASED_CHANGELOG.md
Workflow passes the unreleased changelog path to the validator, checks/commits both files, updates PR comment text, and CONTRIBUTING.md/UNRELEASED_CHANGELOG.md are updated to reflect the new flow.
Regenerated changelog documents (EN/ID)
docs/src/content/docs/changelog.mdx, docs/src/content/docs/id/changelog.mdx
Adds insertion marker/header guidance, normalizes heading levels across release sections, and inserts a few previously missing bullets/headings.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • wailsapp/wails#5269: Both PRs modify CONTRIBUTING.md's v3 changelog guidance to point contributors to v3/UNRELEASED_CHANGELOG.md.
  • wailsapp/wails#5624: Both PRs update CONTRIBUTING.md clarifying auto-managed v3 changelog entries via v3/UNRELEASED_CHANGELOG.md.
  • wailsapp/wails#5665: Appends changelog entries directly into v3/UNRELEASED_CHANGELOG.md, aligning with this PR's new changelog input/output flow.

Suggested labels: Documentation, v3-alpha, size:L

Suggested reviewers: leaanthony

Poem

A rabbit hops through changelog files,
Marking markers, moving miles,
No more edits where they shouldn't be,
Just UNRELEASED_CHANGELOG, wild and free!
🐇✨ headers demoted, entries in place,
A tidier trail for the whole burrow's race.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: generating the docs changelog in a site-friendly format.
Description check ✅ Passed The PR description gives a solid summary and verification, but it leaves the issue reference, type-of-change, and checklist fields unfilled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/changelog-site-format

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@leaanthony leaanthony marked this pull request as ready for review July 5, 2026 10:05
Copilot AI review requested due to automatic review settings July 5, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the v3 release tooling and docs so the site changelog pages render cleanly (no visible “Unreleased” section, no raw comment junk) and the table of contents nests category headings under each version.

Changes:

  • Switch docs changelog generation to insert new releases below a hidden CHANGELOG-INSERT-MARKER and demote changelog category headings to ### for proper ToC nesting.
  • Extend changelog validation to move misplaced entries from the generated docs page back into v3/UNRELEASED_CHANGELOG.md, and update workflow messaging/commits accordingly.
  • Migrate existing English + Indonesian changelog pages to the new marker + heading structure and update contributor guidance.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
v3/UNRELEASED_CHANGELOG.md Adds an unreleased entry describing the new site-friendly changelog generation.
v3/tasks/release/release.go Introduces insert-marker based site changelog updates and category heading demotion.
v3/tasks/release/release_test.go Updates tests to cover marker-based insertion and formatChangelogForSite.
v3/scripts/validate-changelog.go Reworks validator to relocate entries added to generated changelog back into UNRELEASED.
docs/src/content/docs/changelog.mdx Converts the English site changelog page to the new marker format and heading levels.
docs/src/content/docs/id/changelog.mdx Converts the Indonesian site changelog page to the new marker format and heading levels.
CONTRIBUTING.md Clarifies contributor workflow: don’t edit generated changelog page directly.
.github/workflows/changelog-v3.yml Updates validation invocation, commits both files, and improves PR comment output.
Comments suppressed due to low confidence (1)

v3/scripts/validate-changelog.go:139

  • wasAddedInThisPR uses substring matching (strings.Contains) which can flag and remove a pre-existing changelog line if it happens to be a substring of a newly-added line. Since the workflow writes exact added lines (after stripping '+'), this should use exact trimmed equality to avoid false positives that would delete unrelated released entries.
func wasAddedInThisPR(line string, addedLines []string) bool {
	trimmedLine := strings.TrimSpace(line)
	for _, addedLine := range addedLines {
		trimmedAdded := strings.TrimSpace(addedLine)
		if trimmedAdded == trimmedLine {
			return true
		}
		if strings.Contains(trimmedAdded, trimmedLine) && len(trimmedAdded) > 0 {
			return true
		}
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +66
if strings.HasPrefix(strings.TrimSpace(line), "- ") && wasAddedInThisPR(line, addedLines) {
section := currentSection
if section == "" {
Comment on lines 34 to 37
var (
unreleasedChangelogFile = "../../UNRELEASED_CHANGELOG.md"
changelogFile = "../../../docs/src/content/docs/changelog.mdx"
)
Comment on lines +8 to +10
This file is generated by the release workflow. Changelog entries are
collected in `v3/UNRELEASED_CHANGELOG.md` and moved here, reformatted
for the site, when a release is created.
Comment on lines 88 to 90
cd v3/scripts
OUTPUT=$(go run validate-changelog.go ../../docs/src/content/docs/changelog.mdx /tmp/pr_added_lines.txt 2>&1)
OUTPUT=$(go run validate-changelog.go ../../docs/src/content/docs/changelog.mdx /tmp/pr_added_lines.txt ../UNRELEASED_CHANGELOG.md 2>&1)
echo "$OUTPUT"

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (2)
docs/src/content/docs/id/changelog.mdx (2)

1147-1151: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Truncated/malformed changelog entry (mirrors English file).

The v3.0.0-alpha.25 entry is missing its leading - marker and reads as a mid-sentence fragment, matching the issue in docs/src/content/docs/changelog.mdx lines 1235-1239.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/id/changelog.mdx` around lines 1147 - 1151, Fix the
malformed v3.0.0-alpha.25 changelog entry in the localized changelog so it
matches the intended bullet format instead of a stray sentence fragment. Update
the entry under the relevant markdown section in changelog.mdx to include the
missing leading list marker and restore the full translated sentence, keeping it
consistent with the same release entry in the main changelog. Use the existing
v3.0.0-alpha.25 heading and the related list item text as the anchors when
editing.

1131-1141: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Duplicate v3.0.0-alpha.29 version block (mirrors English file).

Same duplicated header/empty-subsections issue as docs/src/content/docs/changelog.mdx lines 1219-1229.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/id/changelog.mdx` around lines 1131 - 1141, The
changelog section has a duplicated v3.0.0-alpha.29 release block with repeated
empty subsections; remove the extra duplicate and keep only one header for that
version. Update the localized changelog entry in changelog.mdx so it matches the
intended sequence around v3.0.0-alpha.29 and the following v3.0.0-alpha.27
entry, using the version headings and subsection labels as the markers to locate
the duplicated block.
🧹 Nitpick comments (2)
docs/src/content/docs/changelog.mdx (1)

1042-1277: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Many empty category headers with no bullets.

Numerous historical version blocks (e.g. v3.0.0-alpha.78 through alpha.65) carry ### Added/### Fixed headers with no bullet content beneath them. Since the site nests these under the version heading in the ToC, readers will see empty subsection entries. Consider stripping empty category headers during generation (in formatChangelogForSite) rather than emitting them verbatim.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/changelog.mdx` around lines 1042 - 1277, The changelog
output includes many empty section headers under version entries, which creates
empty ToC subsections. Update formatChangelogForSite to skip emitting category
headings like Added/Fixed/Changed/Removed when they have no bullet items beneath
them, and ensure the version blocks (such as v3.0.0-alpha.78 through
v3.0.0-alpha.65) only include non-empty sections.
v3/tasks/release/release_test.go (1)

1086-1110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for the missing-marker error path.

TestFormatChangelogForSite covers header demotion well, but there's no test exercising applyChangelogUpdates's new len(split) != 2 error branch (release.go Lines 729-732) when changelogInsertMarker is absent or duplicated from changelogFile. Cheap to add and guards an important failure mode for the new marker-based insertion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/tasks/release/release_test.go` around lines 1086 - 1110, Add a test that
covers the new missing/duplicated marker failure path in applyChangelogUpdates,
since TestFormatChangelogForSite only verifies header formatting. Create a case
where changelogFile does not contain exactly one changelogInsertMarker so the
len(split) != 2 branch is hit, and assert the expected error is returned. Use
applyChangelogUpdates and changelogInsertMarker to locate the logic, and keep
the test focused on the absent or duplicated marker scenario.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/changelog-v3.yml:
- Around line 178-202: The PR comment text in the changelog workflow is escaping
newlines incorrectly, so the `message` built in the `fixed`/`cannot_fix`/`error`
branch renders literal backslash-ns. Update the string concatenation in the
changelog comment builder to use real newline escapes consistently, matching the
earlier `result === 'fixed'` branch, and ensure the `<details>` block and
markdown fence around `fixedContent` in the `message` variable from the workflow
script render as proper markdown.

In `@docs/src/content/docs/changelog.mdx`:
- Around line 1246-1251: The changelog entry is using the wrong list marker and
is categorized under the wrong section. Update the v3.0.0-alpha.22 entry in the
changelog so the “Fix extra-broad Linux package dependencies, fix outdated RPM
dependencies.” item uses the same bullet style as the rest of the file and move
it from the “Changed” section to the “Fixed” section to match its bug-fix
wording.
- Around line 1219-1229: The changelog has a duplicated v3.0.0-alpha.29 block in
the sequence around the alpha.29 entry, so remove the repeated entry and keep
only one occurrence. Update the changelog content in changelog.mdx by collapsing
the duplicate release section into a single v3.0.0-alpha.29 entry, preserving
the surrounding version order and headings.
- Around line 1235-1239: The v3.0.0-alpha.25 changelog entry is truncated and
missing its bullet formatting, so restore the full bullet text in the changelog
section. Update the affected markdown entry in changelog.mdx so the item is a
properly formatted list bullet and the sentence reads as a complete changelog
note, using the existing version heading and surrounding release note style as
the guide.

In `@docs/src/content/docs/id/changelog.mdx`:
- Around line 1158-1163: The changelog entry has a stray plus marker and is
under the wrong section; update the v3.0.0-alpha.22 item in the Indonesian
changelog so the fix line uses the normal bullet marker and appears under ###
Diperbaiki instead of ### Diubah. Make the same content correction consistently
with the corresponding changelog entry elsewhere, using the surrounding section
headers in changelog.mdx to locate the entry.

In `@v3/scripts/validate-changelog.go`:
- Around line 143-196: attemptFix currently makes a non-atomic change by writing
changelogPath after removing issue lines and only then writing unreleasedPath,
so a failure in the second write can lose entries. Update attemptFix to persist
the UNRELEASED_CHANGELOG.md changes first, then write the cleaned changelog
content only after that succeeds, using the existing helpers writeFile,
readFile, and the category insertion logic around
changelogCategories/findCategoryInsertPos.

---

Duplicate comments:
In `@docs/src/content/docs/id/changelog.mdx`:
- Around line 1147-1151: Fix the malformed v3.0.0-alpha.25 changelog entry in
the localized changelog so it matches the intended bullet format instead of a
stray sentence fragment. Update the entry under the relevant markdown section in
changelog.mdx to include the missing leading list marker and restore the full
translated sentence, keeping it consistent with the same release entry in the
main changelog. Use the existing v3.0.0-alpha.25 heading and the related list
item text as the anchors when editing.
- Around line 1131-1141: The changelog section has a duplicated v3.0.0-alpha.29
release block with repeated empty subsections; remove the extra duplicate and
keep only one header for that version. Update the localized changelog entry in
changelog.mdx so it matches the intended sequence around v3.0.0-alpha.29 and the
following v3.0.0-alpha.27 entry, using the version headings and subsection
labels as the markers to locate the duplicated block.

---

Nitpick comments:
In `@docs/src/content/docs/changelog.mdx`:
- Around line 1042-1277: The changelog output includes many empty section
headers under version entries, which creates empty ToC subsections. Update
formatChangelogForSite to skip emitting category headings like
Added/Fixed/Changed/Removed when they have no bullet items beneath them, and
ensure the version blocks (such as v3.0.0-alpha.78 through v3.0.0-alpha.65) only
include non-empty sections.

In `@v3/tasks/release/release_test.go`:
- Around line 1086-1110: Add a test that covers the new missing/duplicated
marker failure path in applyChangelogUpdates, since TestFormatChangelogForSite
only verifies header formatting. Create a case where changelogFile does not
contain exactly one changelogInsertMarker so the len(split) != 2 branch is hit,
and assert the expected error is returned. Use applyChangelogUpdates and
changelogInsertMarker to locate the logic, and keep the test focused on the
absent or duplicated marker scenario.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b99f192-5f41-4dac-85be-18b0526ccfa6

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3e354 and 1323e51.

📒 Files selected for processing (8)
  • .github/workflows/changelog-v3.yml
  • CONTRIBUTING.md
  • docs/src/content/docs/changelog.mdx
  • docs/src/content/docs/id/changelog.mdx
  • v3/UNRELEASED_CHANGELOG.md
  • v3/scripts/validate-changelog.go
  • v3/tasks/release/release.go
  • v3/tasks/release/release_test.go

Comment on lines +178 to +202
message = '## 🔧 Changelog Updated\n\nMisplaced entries were automatically moved to `v3/UNRELEASED_CHANGELOG.md`. The changes have been committed to this PR.';
} else if (result === 'fixed' || result === 'cannot_fix' || result === 'error') {
// Read the fixed changelog content
// Read the fixed unreleased changelog content
const fs = require('fs');
let fixedContent = '';
try {
fixedContent = fs.readFileSync('docs/src/content/docs/changelog.mdx', 'utf8');
fixedContent = fs.readFileSync('v3/UNRELEASED_CHANGELOG.md', 'utf8');
} catch (error) {
fixedContent = 'Error reading fixed changelog content';
}

message = '## ⚠️ Changelog Validation Issue\\n\\n' +
'@' + author + ' Your PR contains changelog entries that were added to already-released versions. These need to be moved to the `[Unreleased]` section.\\n\\n' +
(committed === 'true' ?
'✅ **Auto-fix applied**: The changes have been automatically committed to this PR.' :
'❌ **Manual fix required**: Please apply the changes shown below manually.') + '\\n\\n' +
'@' + author + ' Your PR adds changelog entries to `docs/src/content/docs/changelog.mdx`. That file is generated by the release workflow and only contains released versions, so entries added there directly would be lost. New entries belong in `v3/UNRELEASED_CHANGELOG.md`.\\n\\n' +
(committed === 'true' ?
'✅ **Auto-fix applied**: The entries have been moved to `v3/UNRELEASED_CHANGELOG.md` and committed to this PR.' :
'❌ **Manual fix required**: Please move your entries to `v3/UNRELEASED_CHANGELOG.md` as shown below.') + '\\n\\n' +
'<details>\\n' +
'<summary>📝 Click to see the corrected changelog content</summary>\\n\\n' +
'```mdx\\n' +
'<summary>📝 Click to see the corrected v3/UNRELEASED_CHANGELOG.md content</summary>\\n\\n' +
'```md\\n' +
fixedContent +
'\\n```\\n\\n' +
'</details>\\n\\n' +
'**What happened?** \\n' +
'The validation script detected that you added changelog entries to a version section that has already been released (like `v3.0.0-alpha.10`). All new entries should go in the `[Unreleased]` section under the appropriate category (`### Added`, `### Fixed`, etc.).\\n\\n' +
(committed !== 'true' ? '**Action needed:** Please copy the corrected content from above and replace your changelog file.' : '');
'The validation script detected changelog entries added directly to the generated changelog page. All new entries should go in `v3/UNRELEASED_CHANGELOG.md` under the appropriate category (`## Added`, `## Fixed`, etc.); they are moved to the site changelog automatically when a release is created.\\n\\n' +
(committed !== 'true' ? '**Action needed:** Please remove the entries from `docs/src/content/docs/changelog.mdx` and add them to `v3/UNRELEASED_CHANGELOG.md` as shown above.' : '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Literal \n will leak into the PR comment instead of newlines/code fences.

The fixed/cannot_fix/error branch (lines 189-202) uses \\n\\n (double backslash) throughout, while the fixed && committed branch at line 178 correctly uses \n\n (single backslash). In a JS string literal, \\n produces the literal two characters \ and n, not a newline. This breaks the intended formatting of the comment body, including the markdown code fence around the changelog snippet (```md\\n ... \\n```\\n\\n), which will render as garbled text with visible backslash-n sequences instead of a proper collapsible code block.

🛠️ Proposed fix
-            message = '## ⚠️ Changelog Validation Issue\\n\\n' +
-              '@' + author + ' Your PR adds changelog entries to `docs/src/content/docs/changelog.mdx`. That file is generated by the release workflow and only contains released versions, so entries added there directly would be lost. New entries belong in `v3/UNRELEASED_CHANGELOG.md`.\\n\\n' +
-              (committed === 'true' ?
-                '✅ **Auto-fix applied**: The entries have been moved to `v3/UNRELEASED_CHANGELOG.md` and committed to this PR.' :
-                '❌ **Manual fix required**: Please move your entries to `v3/UNRELEASED_CHANGELOG.md` as shown below.') + '\\n\\n' +
-              '<details>\\n' +
-              '<summary>📝 Click to see the corrected v3/UNRELEASED_CHANGELOG.md content</summary>\\n\\n' +
-              '```md\\n' +
-              fixedContent +
-              '\\n```\\n\\n' +
-              '</details>\\n\\n' +
-              '**What happened?** \\n' +
-              'The validation script detected changelog entries added directly to the generated changelog page. All new entries should go in `v3/UNRELEASED_CHANGELOG.md` under the appropriate category (`## Added`, `## Fixed`, etc.); they are moved to the site changelog automatically when a release is created.\\n\\n' +
-              (committed !== 'true' ? '**Action needed:** Please remove the entries from `docs/src/content/docs/changelog.mdx` and add them to `v3/UNRELEASED_CHANGELOG.md` as shown above.' : '');
+            message = '## ⚠️ Changelog Validation Issue\n\n' +
+              '@' + author + ' Your PR adds changelog entries to `docs/src/content/docs/changelog.mdx`. That file is generated by the release workflow and only contains released versions, so entries added there directly would be lost. New entries belong in `v3/UNRELEASED_CHANGELOG.md`.\n\n' +
+              (committed === 'true' ?
+                '✅ **Auto-fix applied**: The entries have been moved to `v3/UNRELEASED_CHANGELOG.md` and committed to this PR.' :
+                '❌ **Manual fix required**: Please move your entries to `v3/UNRELEASED_CHANGELOG.md` as shown below.') + '\n\n' +
+              '<details>\n' +
+              '<summary>📝 Click to see the corrected v3/UNRELEASED_CHANGELOG.md content</summary>\n\n' +
+              '```md\n' +
+              fixedContent +
+              '\n```\n\n' +
+              '</details>\n\n' +
+              '**What happened?** \n' +
+              'The validation script detected changelog entries added directly to the generated changelog page. All new entries should go in `v3/UNRELEASED_CHANGELOG.md` under the appropriate category (`## Added`, `## Fixed`, etc.); they are moved to the site changelog automatically when a release is created.\n\n' +
+              (committed !== 'true' ? '**Action needed:** Please remove the entries from `docs/src/content/docs/changelog.mdx` and add them to `v3/UNRELEASED_CHANGELOG.md` as shown above.' : '');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/changelog-v3.yml around lines 178 - 202, The PR comment
text in the changelog workflow is escaping newlines incorrectly, so the
`message` built in the `fixed`/`cannot_fix`/`error` branch renders literal
backslash-ns. Update the string concatenation in the changelog comment builder
to use real newline escapes consistently, matching the earlier `result ===
'fixed'` branch, and ensure the `<details>` block and markdown fence around
`fixedContent` in the `message` variable from the workflow script render as
proper markdown.

Comment on lines 1219 to 1229
## v3.0.0-alpha.29 - 2025-09-25

## Added

## Changed

## Fixed

### Added
### Changed
### Fixed
## v3.0.0-alpha.29 - 2025-09-25

## Added

## Changed

## Fixed

### Added
### Changed
### Fixed
## v3.0.0-alpha.27 - 2025-09-07

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="docs/src/content/docs/changelog.mdx"

# Show the relevant section with line numbers.
sed -n '1200,1240p' "$file" | cat -n

echo
echo "---- exact occurrences of the alpha.29 header ----"
grep -nF '## v3.0.0-alpha.29 - 2025-09-25' "$file" || true

echo
echo "---- nearby context around each occurrence ----"
python3 - <<'PY'
from pathlib import Path
path = Path("docs/src/content/docs/changelog.mdx")
lines = path.read_text().splitlines()
needle = "## v3.0.0-alpha.29 - 2025-09-25"
for i, line in enumerate(lines, 1):
    if line == needle:
        print(f"\n=== occurrence at line {i} ===")
        for j in range(max(1, i-2), min(len(lines), i+8)+1):
            print(f"{j:5d}: {lines[j-1]}")
PY

Repository: wailsapp/wails

Length of output: 1741


Remove the duplicate v3.0.0-alpha.29 entry
docs/src/content/docs/changelog.mdx repeats the same v3.0.0-alpha.29 - 2025-09-25 block twice in a row; collapse it to a single changelog entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/changelog.mdx` around lines 1219 - 1229, The changelog
has a duplicated v3.0.0-alpha.29 block in the sequence around the alpha.29
entry, so remove the repeated entry and keep only one occurrence. Update the
changelog content in changelog.mdx by collapsing the duplicate release section
into a single v3.0.0-alpha.29 entry, preserving the surrounding version order
and headings.

Comment on lines 1235 to 1239
## v3.0.0-alpha.25 - 2025-08-16

## Changed
### Changed
no longer ignored, and override the config value.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for the original/full text of this entry in git history or UNRELEASED_CHANGELOG history
rg -n "no longer ignored, and override the config value" -C5

Repository: wailsapp/wails

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact changelog section around the cited lines.
sed -n '1228,1245p' docs/src/content/docs/changelog.mdx

# Search for nearby wording and the same release heading elsewhere in the repo.
rg -n "v3\.0\.0-alpha\.25|no longer ignored|override the config value" docs src -C 3

# If git history is available, look for the same release entry in prior versions of this file.
git log --oneline -- docs/src/content/docs/changelog.mdx | head -n 20

Repository: wailsapp/wails

Length of output: 1682


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the exact local context with line numbers.
sed -n '1230,1241p' docs/src/content/docs/changelog.mdx | cat -n

# Check the same release block in the Indonesian changelog for parity.
sed -n '1144,1153p' docs/src/content/docs/id/changelog.mdx | cat -n

# Search for any fuller version of the fragment in the repository.
rg -n "no longer ignored, and override the config value|ignored, and override the config value|override the config value" docs -C 2

Repository: wailsapp/wails

Length of output: 1293


Restore the missing changelog bullet text. The v3.0.0-alpha.25 entry is truncated here: the bullet marker is missing and the sentence starts mid-thought.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/changelog.mdx` around lines 1235 - 1239, The
v3.0.0-alpha.25 changelog entry is truncated and missing its bullet formatting,
so restore the full bullet text in the changelog section. Update the affected
markdown entry in changelog.mdx so the item is a properly formatted list bullet
and the sentence reads as a complete changelog note, using the existing version
heading and surrounding release note style as the guide.

Comment on lines 1246 to 1251
## v3.0.0-alpha.22 - 2025-08-10

## Added

## Changed
### Added
### Changed
+ Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stray + bullet marker and miscategorized entry.

Line 1250 uses + instead of - (the marker used everywhere else in the file), and the entry text ("Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.") describes a bug fix but is placed under ### Changed rather than ### Fixed.

🐛 Proposed fix
 ### Added
-### Changed
-+ Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.
+### Changed
+
+### Fixed
+- Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## v3.0.0-alpha.22 - 2025-08-10
## Added
## Changed
### Added
### Changed
+ Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.
## v3.0.0-alpha.22 - 2025-08-10
### Added
### Changed
### Fixed
- Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/changelog.mdx` around lines 1246 - 1251, The changelog
entry is using the wrong list marker and is categorized under the wrong section.
Update the v3.0.0-alpha.22 entry in the changelog so the “Fix extra-broad Linux
package dependencies, fix outdated RPM dependencies.” item uses the same bullet
style as the rest of the file and move it from the “Changed” section to the
“Fixed” section to match its bug-fix wording.

Comment on lines 1158 to 1163
## v3.0.0-alpha.22 - 2025-08-10

## Ditambahkan

## Diubah
### Ditambahkan
### Diubah
+ Fix extra-broad Linux package dependencies, fix outdated RPM dependencies.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stray + bullet marker and miscategorized entry (mirrors English file).

Line 1162 uses + instead of -, and the fix-related entry is placed under ### Diubah (Changed) instead of ### Diperbaiki (Fixed). Same content defect as docs/src/content/docs/changelog.mdx lines 1246-1251 — fix in both places consistently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/src/content/docs/id/changelog.mdx` around lines 1158 - 1163, The
changelog entry has a stray plus marker and is under the wrong section; update
the v3.0.0-alpha.22 item in the Indonesian changelog so the fix line uses the
normal bullet marker and appears under ### Diperbaiki instead of ### Diubah.
Make the same content correction consistently with the corresponding changelog
entry elsewhere, using the surrounding section headers in changelog.mdx to
locate the entry.

Comment on lines +143 to +196
// attemptFix removes the misplaced entries from the generated changelog and
// inserts them into the matching category sections of UNRELEASED_CHANGELOG.md.
func attemptFix(content string, issues []Issue, changelogPath, unreleasedPath string) (bool, error) {
lines := strings.Split(content, "\n")

// Find unreleased section
unreleasedStart := -1
unreleasedEnd := -1

for i, line := range lines {
if strings.Contains(line, "[Unreleased]") {
unreleasedStart = i
for j := i + 1; j < len(lines); j++ {
if strings.HasPrefix(lines[j], "## ") && !strings.Contains(lines[j], "[Unreleased]") {
unreleasedEnd = j
break
}
}
break
}
}

if unreleasedStart == -1 {
return false, fmt.Errorf("Could not find [Unreleased] section")
}

// Group issues by category
issuesByCategory := make(map[string][]Issue)
for _, issue := range issues {
issuesByCategory[issue.Category] = append(issuesByCategory[issue.Category], issue)
}

// Remove issues from original locations (in reverse order)
var linesToRemove []int
// Remove issue lines from the changelog (highest line number first)
linesToRemove := make([]int, 0, len(issues))
for _, issue := range issues {
linesToRemove = append(linesToRemove, issue.Line)
}

// Sort in reverse order
for i := 0; i < len(linesToRemove); i++ {
for j := i + 1; j < len(linesToRemove); j++ {
if linesToRemove[i] < linesToRemove[j] {
linesToRemove[i], linesToRemove[j] = linesToRemove[j], linesToRemove[i]
}
}
}

// Remove lines
for _, lineNum := range linesToRemove {
lines = append(lines[:lineNum], lines[lineNum+1:]...)
}

// Add entries to unreleased section
for category, categoryIssues := range issuesByCategory {
categoryFound := false
insertPos := unreleasedStart + 1

for i := unreleasedStart + 1; i < unreleasedEnd && i < len(lines); i++ {
if strings.Contains(lines[i], "### "+category) || strings.Contains(lines[i], "## "+category) {
categoryFound = true
for j := i + 1; j < unreleasedEnd && j < len(lines); j++ {
if strings.HasPrefix(lines[j], "### ") || strings.HasPrefix(lines[j], "## ") {
insertPos = j
break
}
if j == len(lines)-1 || j == unreleasedEnd-1 {
insertPos = j + 1
break
}
}
break
}
}
// Insert the entries into UNRELEASED_CHANGELOG.md
unreleasedContent, err := readFile(unreleasedPath)
if err != nil {
return false, fmt.Errorf("failed to read %s: %w", unreleasedPath, err)
}
unreleasedLines := strings.Split(unreleasedContent, "\n")

if !categoryFound {
if unreleasedEnd > 0 {
insertPos = unreleasedEnd
} else {
insertPos = unreleasedStart + 1
}
issuesByCategory := make(map[string][]Issue)
for _, issue := range issues {
issuesByCategory[issue.Category] = append(issuesByCategory[issue.Category], issue)
}

newLines := []string{
"",
"### " + category,
"",
}
lines = append(lines[:insertPos], append(newLines, lines[insertPos:]...)...)
insertPos += len(newLines)
unreleasedEnd += len(newLines)
// Iterate categories in a stable order
for _, category := range changelogCategories {
categoryIssues := issuesByCategory[category]
if len(categoryIssues) == 0 {
continue
}
insertPos := findCategoryInsertPos(unreleasedLines, category)
if insertPos == -1 {
return false, fmt.Errorf("could not find '## %s' section in %s", category, unreleasedPath)
}

// Add entries to the category
for _, issue := range categoryIssues {
lines = append(lines[:insertPos], append([]string{issue.Content}, lines[insertPos:]...)...)
unreleasedLines = append(unreleasedLines[:insertPos], append([]string{issue.Content}, unreleasedLines[insertPos:]...)...)
insertPos++
unreleasedEnd++
}
}

// Write back to file
newContent := strings.Join(lines, "\n")
return true, writeFile(outputPath, newContent)
if err := writeFile(changelogPath, strings.Join(lines, "\n")); err != nil {
return false, err
}
return true, writeFile(unreleasedPath, strings.Join(unreleasedLines, "\n"))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Non-atomic writes risk permanently losing changelog entries.

attemptFix writes changelogPath (with the misplaced lines already removed) before writing unreleasedPath (with those same lines inserted). If the second writeFile call fails for any reason (permissions, disk full, path issue), the entries have already been stripped from changelog.mdx on disk but were never persisted to UNRELEASED_CHANGELOG.md — they're gone. Since main exits on this error path regardless of the returned fixed bool, there's no recovery.

Writing the destination file first (and only then removing content from the source) makes the failure mode "duplicated entry" instead of "lost entry."

🛡️ Proposed fix: write unreleasedPath before changelogPath
-	if err := writeFile(changelogPath, strings.Join(lines, "\n")); err != nil {
-		return false, err
-	}
-	return true, writeFile(unreleasedPath, strings.Join(unreleasedLines, "\n"))
+	// Write the destination first: if this fails, changelog.mdx is left
+	// untouched and no entries are lost. Writing changelog.mdx first would
+	// risk permanently losing entries if this write then failed.
+	if err := writeFile(unreleasedPath, strings.Join(unreleasedLines, "\n")); err != nil {
+		return false, err
+	}
+	return true, writeFile(changelogPath, strings.Join(lines, "\n"))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// attemptFix removes the misplaced entries from the generated changelog and
// inserts them into the matching category sections of UNRELEASED_CHANGELOG.md.
func attemptFix(content string, issues []Issue, changelogPath, unreleasedPath string) (bool, error) {
lines := strings.Split(content, "\n")
// Find unreleased section
unreleasedStart := -1
unreleasedEnd := -1
for i, line := range lines {
if strings.Contains(line, "[Unreleased]") {
unreleasedStart = i
for j := i + 1; j < len(lines); j++ {
if strings.HasPrefix(lines[j], "## ") && !strings.Contains(lines[j], "[Unreleased]") {
unreleasedEnd = j
break
}
}
break
}
}
if unreleasedStart == -1 {
return false, fmt.Errorf("Could not find [Unreleased] section")
}
// Group issues by category
issuesByCategory := make(map[string][]Issue)
for _, issue := range issues {
issuesByCategory[issue.Category] = append(issuesByCategory[issue.Category], issue)
}
// Remove issues from original locations (in reverse order)
var linesToRemove []int
// Remove issue lines from the changelog (highest line number first)
linesToRemove := make([]int, 0, len(issues))
for _, issue := range issues {
linesToRemove = append(linesToRemove, issue.Line)
}
// Sort in reverse order
for i := 0; i < len(linesToRemove); i++ {
for j := i + 1; j < len(linesToRemove); j++ {
if linesToRemove[i] < linesToRemove[j] {
linesToRemove[i], linesToRemove[j] = linesToRemove[j], linesToRemove[i]
}
}
}
// Remove lines
for _, lineNum := range linesToRemove {
lines = append(lines[:lineNum], lines[lineNum+1:]...)
}
// Add entries to unreleased section
for category, categoryIssues := range issuesByCategory {
categoryFound := false
insertPos := unreleasedStart + 1
for i := unreleasedStart + 1; i < unreleasedEnd && i < len(lines); i++ {
if strings.Contains(lines[i], "### "+category) || strings.Contains(lines[i], "## "+category) {
categoryFound = true
for j := i + 1; j < unreleasedEnd && j < len(lines); j++ {
if strings.HasPrefix(lines[j], "### ") || strings.HasPrefix(lines[j], "## ") {
insertPos = j
break
}
if j == len(lines)-1 || j == unreleasedEnd-1 {
insertPos = j + 1
break
}
}
break
}
}
// Insert the entries into UNRELEASED_CHANGELOG.md
unreleasedContent, err := readFile(unreleasedPath)
if err != nil {
return false, fmt.Errorf("failed to read %s: %w", unreleasedPath, err)
}
unreleasedLines := strings.Split(unreleasedContent, "\n")
if !categoryFound {
if unreleasedEnd > 0 {
insertPos = unreleasedEnd
} else {
insertPos = unreleasedStart + 1
}
issuesByCategory := make(map[string][]Issue)
for _, issue := range issues {
issuesByCategory[issue.Category] = append(issuesByCategory[issue.Category], issue)
}
newLines := []string{
"",
"### " + category,
"",
}
lines = append(lines[:insertPos], append(newLines, lines[insertPos:]...)...)
insertPos += len(newLines)
unreleasedEnd += len(newLines)
// Iterate categories in a stable order
for _, category := range changelogCategories {
categoryIssues := issuesByCategory[category]
if len(categoryIssues) == 0 {
continue
}
insertPos := findCategoryInsertPos(unreleasedLines, category)
if insertPos == -1 {
return false, fmt.Errorf("could not find '## %s' section in %s", category, unreleasedPath)
}
// Add entries to the category
for _, issue := range categoryIssues {
lines = append(lines[:insertPos], append([]string{issue.Content}, lines[insertPos:]...)...)
unreleasedLines = append(unreleasedLines[:insertPos], append([]string{issue.Content}, unreleasedLines[insertPos:]...)...)
insertPos++
unreleasedEnd++
}
}
// Write back to file
newContent := strings.Join(lines, "\n")
return true, writeFile(outputPath, newContent)
if err := writeFile(changelogPath, strings.Join(lines, "\n")); err != nil {
return false, err
}
return true, writeFile(unreleasedPath, strings.Join(unreleasedLines, "\n"))
}
// attemptFix removes the misplaced entries from the generated changelog and
// inserts them into the matching category sections of UNRELEASED_CHANGELOG.md.
func attemptFix(content string, issues []Issue, changelogPath, unreleasedPath string) (bool, error) {
lines := strings.Split(content, "\n")
// Remove issue lines from the changelog (highest line number first)
linesToRemove := make([]int, 0, len(issues))
for _, issue := range issues {
linesToRemove = append(linesToRemove, issue.Line)
}
for i := 0; i < len(linesToRemove); i++ {
for j := i + 1; j < len(linesToRemove); j++ {
if linesToRemove[i] < linesToRemove[j] {
linesToRemove[i], linesToRemove[j] = linesToRemove[j], linesToRemove[i]
}
}
}
for _, lineNum := range linesToRemove {
lines = append(lines[:lineNum], lines[lineNum+1:]...)
}
// Insert the entries into UNRELEASED_CHANGELOG.md
unreleasedContent, err := readFile(unreleasedPath)
if err != nil {
return false, fmt.Errorf("failed to read %s: %w", unreleasedPath, err)
}
unreleasedLines := strings.Split(unreleasedContent, "\n")
issuesByCategory := make(map[string][]Issue)
for _, issue := range issues {
issuesByCategory[issue.Category] = append(issuesByCategory[issue.Category], issue)
}
// Iterate categories in a stable order
for _, category := range changelogCategories {
categoryIssues := issuesByCategory[category]
if len(categoryIssues) == 0 {
continue
}
insertPos := findCategoryInsertPos(unreleasedLines, category)
if insertPos == -1 {
return false, fmt.Errorf("could not find '## %s' section in %s", category, unreleasedPath)
}
for _, issue := range categoryIssues {
unreleasedLines = append(unreleasedLines[:insertPos], append([]string{issue.Content}, unreleasedLines[insertPos:]...)...)
insertPos++
}
}
// Write the destination first: if this fails, changelog.mdx is left
// untouched and no entries are lost. Writing changelog.mdx first would
// risk permanently losing entries if this write then failed.
if err := writeFile(unreleasedPath, strings.Join(unreleasedLines, "\n")); err != nil {
return false, err
}
return true, writeFile(changelogPath, strings.Join(lines, "\n"))
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@v3/scripts/validate-changelog.go` around lines 143 - 196, attemptFix
currently makes a non-atomic change by writing changelogPath after removing
issue lines and only then writing unreleasedPath, so a failure in the second
write can lose entries. Update attemptFix to persist the UNRELEASED_CHANGELOG.md
changes first, then write the cleaned changelog content only after that
succeeds, using the existing helpers writeFile, readFile, and the category
insertion logic around changelogCategories/findCategoryInsertPos.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants