From d5668dcba1d7a8a2e55c3e7cac54388a43302c3d Mon Sep 17 00:00:00 2001 From: zhanghui Date: Fri, 7 Aug 2026 11:01:40 +0800 Subject: [PATCH] ci: take the release page's prose from the CHANGELOG too (#391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Leaving the lead summary and the upgrade notes to be composed at publish time put the writing at the worst possible moment — weeks after the changes, with no review. Both belong in the CHANGELOG entry, which the release PR is already editing. The lead needed no code: text between the version heading and the first group already flowed into the page. The Upgrade group did — CI appended its own `## Upgrade`, so a CHANGELOG that carried upgrade notes produced two headings. The group is now lifted out and the boilerplate wrapped around it, pip line above, compare link below, matching every page since 1.1.3. Publishing is now a read-through and a click. Co-authored-by: zhanghui Co-authored-by: Claude Opus 5 (1M context) --- .claude/skills/release/SKILL.md | 50 ++++++++++++++++++++------------- .github/workflows/release.yml | 15 ++++++++-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/.claude/skills/release/SKILL.md b/.claude/skills/release/SKILL.md index 9022604..8d1f2c2 100644 --- a/.claude/skills/release/SKILL.md +++ b/.claude/skills/release/SKILL.md @@ -27,16 +27,16 @@ click **Publish**. 1. Bump the version → pyproject.toml [project] version = "X.Y.Z" (single source; everos.__version__ reads installed package metadata) 2. Update CHANGELOG.md → move the Unreleased entries under a new - ## [X.Y.Z] - heading + ## [X.Y.Z] - heading, and write the release page's prose here + (lead paragraph + `### Upgrade` group — see "The release page") 3. Commit → git commit -m "chore(release): vX.Y.Z" 4. Open a PR, merge to main after green CI 5. Tag main + push → git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z 6. Approve → the release.yml run pauses on the `release` environment; a reviewer approves in the Actions run 7. Verify → https://pypi.org/project/everos/X.Y.Z/ -8. Publish the page → the run leaves a DRAFT GitHub Release titled - "EverOS X.Y.Z", body prefilled from the CHANGELOG section. Write the lead - summary at the top, then click Publish. +8. Publish the page → the run leaves a DRAFT GitHub Release, already + complete if step 2 was done properly. Read it once, click Publish. ``` The tag must equal the `pyproject.toml` version — the workflow refuses to @@ -45,25 +45,37 @@ section fails the release job for the same reason. ## The release page -Every page has the same shape: +**The whole page is written in CHANGELOG.md, during the release PR.** Nothing +is meant to be composed at publish time — by then the changes are weeks old and +the text gets no review. Write the version section like this: -``` - prose — CI cannot write this -## Added / ## Changed / ## Fixed / ... from the CHANGELOG section, verbatim -## Upgrade pip line + compare link prefilled; migration notes by hand +```markdown +## [X.Y.Z] - 2026-09-01 + +**What this release is for.** One paragraph, prose, no bullets — it becomes the +lead of the release page. Say what changed for a user, not what was refactored. + +### Added +### Changed +### Fixed + +### Upgrade + +What a reader must know before upgrading: what happens on first startup, which +command recovers a bad state, which pins moved. Omit the group entirely when a +plain `pip install --upgrade` is all there is — 1.1.4 and 1.2.0 have nothing +here. Do not write filler. ``` -Two parts are yours to write in the draft: +CI turns that into the page: everything above `### Upgrade` is lifted verbatim +with the group headings demoted to `##`, and the Upgrade prose is wrapped in the +boilerplate — pip line above it, compare link below — which is the shape every +release since 1.1.3 has. See +[1.2.1](https://github.com/EverMind-AI/EverOS/releases/tag/v1.2.1). -- **The lead summary** — the two or three sentences above the first heading - saying what this release is for. -- **Migration notes in `## Upgrade`**, when the release has any: what happens on - the first startup after upgrading, which command recovers a bad state, which - pins moved. Skip when a plain `pip install --upgrade` is genuinely all there - is; do not invent filler. - -See [1.2.1](https://github.com/EverMind-AI/EverOS/releases/tag/v1.2.1) for the -shape of both. +So publishing is a read-through and a click. If the draft looks wrong, the fix +belongs in CHANGELOG.md on `main`, not only in the draft — otherwise the two +drift apart and the next release inherits the habit. > While it is a draft, GitHub serves the release at > `releases/tag/untagged-`, and that URL keeps serving a stale page after diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f912f36..01fa5a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -109,8 +109,17 @@ jobs: body = re.sub(r"^### ", "## ", match.group(1).strip(), flags=re.M) + "\n" # Every EverOS release page since 1.1.3 closes with an Upgrade - # section. Only its boilerplate is prefilled here — a release that - # needs migration notes gets them written into the draft by hand. + # section: the pip line, then whatever this release needs a reader to + # know before upgrading, then the compare link. The prose comes from + # an `### Upgrade` group in the CHANGELOG section — written in the + # release PR, where it gets reviewed — and is lifted out here so the + # boilerplate can be wrapped around it instead of colliding with it. + upgrade = "" + found = re.search(r"^## Upgrade\n(.*?)(?=^## |\Z)", body, re.S | re.M) + if found is not None: + upgrade = found.group(1).strip() + body = (body[: found.start()] + body[found.end() :]).rstrip() + "\n" + tags = subprocess.run( ["git", "tag", "--list", "v*", "--sort=-v:refname"], capture_output=True, text=True, check=True, @@ -123,6 +132,8 @@ jobs: ) body += "\n## Upgrade\n\n```bash\npip install --upgrade everos # or: uv sync\n```\n" + if upgrade: + body += f"\n{upgrade}\n" if previous is not None: compare = f"https://github.com/{os.environ['REPO']}/compare/{previous}...{tag}" body += f"\n**Full changelog:** [{previous}...{tag}]({compare})\n"