Skip to content

feat(release): 支持版本后缀与可选自动发布说明 - #247

Closed
Gu-ZT wants to merge 1 commit into
Anvil-Dev:dev/26.1from
Gu-ZT:codex/release-prerelease-notes
Closed

Gu-ZT wants to merge 1 commit into
Anvil-Dev:dev/26.1from
Gu-ZT:codex/release-prerelease-notes

Conversation

@Gu-ZT

@Gu-ZT Gu-ZT commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

发布总控现在按七条分支一致的 mod_version 识别版本类型,并支持可选的默认分支发布说明。

版本格式 GitHub 类型 升 patch
X.Y.Z+alpha.N、X.Y.Z+beta.N、X.Y.Z+pre-release.N、X.Y.Z+rc.N Pre-release 否
X.Y.Z+build.N、X.Y.Z+hotfix.N Release 否
X.Y.Z Release 是

alpha 在两站上架为 Alpha,beta、pre-release 和 rc 上架为 Beta,正式类型上架为 Release。补发和回执核对使用同一规则。全部类型均等待七个 Central 部署成功后公开 GitHub Release,纯版本的升版 PR 继续使用 [skip ci]。

新的 generate_release_notes 开关默认关闭。开启后调用 GitHub 原生 releases/generate-notes,以锁定的 dev/26.1 SHA 生成内容,正文保留 GitHub 的 PR、贡献者和完整变更链接,并附构件校验表。生成结果写入发布计划和 tag 注释,续跑复用。预检任务因此需要 GitHub 对此接口要求的 contents: write 权限。

本地 19 项发布回归检查、2 项 CI 发布检查和 Actions 语法检查通过。用实际 GitHub 接口预览了 v2.0.0+beta.1 的说明,包含默认分支 PR,七种类型、跳过升版和预发布补发均有覆盖。发布脚本的回归检查也接入了本 PR 的 CI。

另以 2.0.0+rc.1 完成了 stageRelease 实际构建,22 组 Maven 发布坐标、主包内 21 个模块、源码和 Javadoc 附件均通过构件校验,完整的加号后缀得到保留。

- 按加号后缀区分预发布、正式构建和热修复版本,仅纯版本创建 patch 升版 PR
- 可选调用 GitHub 原生接口生成默认分支发布说明并冻结结果供续跑复用
- 保持两站类型及补发回执一致,补充发布规则和回归检查
@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
cube ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering ✅ Compatible 0
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
math ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
ui ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308 Gugle2308 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

本地实跑验证:19 项回归测试全部通过(10 项既有 + 9 项新增,0.115s)。版本规则和发布类型判断与文档表格完全一致,但 generate-notes 没有传 previous_tag_name,连续多个资质版本的说明可能重叠;另外,+build.N、+hotfix.N 会取代 GitHub 的 Latest 标记,这与“Latest 由正式版维护”的表述需要确认意图。

审查范围:base dev/26.1 → head 046fa4da,10 文件 +281/−19(patch-diff 与 API 声明逐位一致)、draft=false、mergeable=true、state=open、无既有 review。

✅ 一、实跑结果:19 项全过(我把 head 的 6 个脚本抽出来本地执行)

Ran 19 tests in 0.115s — OK

10 项既有测试(test_release.py)+ 9 项新增(test_release_options.py)全部通过,说明本次改动没有破坏既有的封存、续传、Central 回执、两站重传语义。

✅ 二、逐项核对结论

检查点 结论
版本正则 7 组合法形式 2.0.0 / +alpha.1 / +beta.2 / +pre-release.3 / +rc.4 / +build.5 / +hotfix.6 全部接受,且 mod_version() 与 release_type() 结论一致
版本正则 8 组非法形式 -beta.1(连字符)、+alpha(缺 N)、+rc.01(前导零)、+beta.-1、+snapshot.5、+unknown.1、+build.1+rc.2(双后缀)、+hotfix.1/other(路径穿越)全部拒绝 ✅
类型映射 alpha→alpha、beta/pre-release/rc→beta、build/hotfix/无限定→release,与 README 三列表格逐行一致
升 patch 范围 仅纯 X.Y.Z 升级;release.py:47 生成 bump_patch,release.yml:178 用该输出做 job 级开关,bump() 内再拦一次 if "+" in version(双保险,测试验证 github/contents 均未被调用)
两站类型同源 Modrinth version_type 与 CurseForge releaseType 复用同一个 kind;测试断言 CurseForge 的三次上传(主包 + sources + javadoc)releaseType 全一致 ✅
Modrinth 幂等对账 远端已是 beta 时直接复用不回传;类型不符报 different release metadata ✅
续跑不重复生成 prepare() 命中 source-plan 时不再调 generate-notes(测试断言 github.call_count) ✅
封存幂等 连续两次 seal() 只产生 1 个 tag + 1 个 draft,正文中生成说明只出现 1 次(不重复拼接) ✅
类型漂移防护 seal()/publish() 均校验 release.prerelease,不匹配直接报 Conflicting release type,且测试验证此时不发任何 PATCH ✅
Central 门槛 七个 release/<tag>/<line>/central 全部 success 才解除草稿,与文档一致 ✅
CI 接入 build_and_test.yml 新增 discover -s .github/release;pull_request.yml 新增 .github/release/** 与两个 workflow 路径 ⇒ 本 PR 自身也会跑这些测试 ✅

⚠️ 三、generate-notes 未传 previous_tag_name:连续资质版本的说明会重叠

# release.py:41
plan["release_notes"] = github("releases/generate-notes", {
    "tag_name": plan["tag"], "target_commitish": targets[ANCHOR]["sha"],
})["body"]

只传了 tag_name + target_commitish,"上一个 tag"完全交给 GitHub 自动挑选。PR 只预览了 v2.0.0+beta.1 一次(此时上一个 tag 是普通版,结论正确),但连续两版资质版本没有实测:

  • v2.0.0+beta.1 → v2.0.0+rc.1 → v2.0.0 三连发时,若 GitHub 因 + 后缀或 prerelease 属性跳过上一版 tag,rc.1 的说明会把 beta 阶段的 PR 再列一遍;正式版又会把 beta+rc 全列一遍。
  • 该接口本身支持 previous_tag_name,成本极低。

建议:显式传 "previous_tag_name": <上一个已发布 tag>(可从 releases 列表取最新非 draft),或补一次"连续两版"的实测再合。

⚠️ 四、+build.N / +hotfix.N 会抢 GitHub Latest,请确认意图

# release.py:137 / 145
prerelease = release_type(plan["version"]) != "release"   # build/hotfix → False
...
"make_latest": "false" if prerelease else "true"

而 PR 表格把 X.Y.Z+build.N、X.Y.Z+hotfix.N 归为 Release 类型,所以它们会以 make_latest: "true" 发布 ⇒ 把 v2.0.0 从 Releases 页的 Latest 位置挤下去。

RELEASING.md 写的是「Pre-release 使用独立 tag,GitHub 的 Latest 标记由正式版维护」。若"正式版"指 X.Y.Z,此处与代码不符;若指"Release 类型",则文档措辞需要更明确。另外 +build.N(同版本重建)夺走 Latest 在产品意义上值得商榷,而 +hotfix.N 夺走 Latest 更合理。

建议:若意图是"只有纯 X.Y.Z 维护 Latest",改成 prerelease = "+" in plan["version"](但注意这同时会把 build/hotfix 变成 GitHub Pre-release,与表格冲突)——更稳的做法是拆成两个独立判断:prerelease(GitHub)与 take_latest,并在文档里写清 build/hotfix 的 Latest 归属。

⚠️ 五、资质版本跳过 bump ⇒ 分支停留在已发布版本,内部快照与 Central 同号

+ 版本一律跳过升版(设计如此),但七条分支的 mod_version 会继续停留在已发布版本,而:

// build.gradle:12
version = mod_version
// ci.gradle:39(内部快照发布)
version: main.version.toString()

即内部 Maven 快照会以 2.0.0+beta.1 这一已在 Central 发布的坐标继续产出。RELEASING.md 只写了「后续发 2.0.0+rc.1 或 2.0.0 时,再将七条分支同步到对应版本」,没有要求"发完立即同步"。

建议:在 RELEASING.md 明确"qualified 版本发布后应立即把七条分支同步到下一个版本"(否则该窗口内的内部快照与正式版同号),或让 bump() 对 qualified 版本改为升后缀序号(beta.1 → beta.2)而非整体跳过。

💡 六、其余建议

  1. 最小权限回退:prepare 现在无条件 contents: write(release.yml:32),而 notes 默认关闭。GitHub 不支持条件 permissions,可考虑把 generate-notes 拆到单独的 job(带 write)后再回传,或至少在 workflow 顶部注明"默认关闭时该权限仍被授予"。
  2. release_url() 未编码 tag(mod_sites.py:117-118):
    return f"https://github.com/{repo()}/releases/tag/{plan['tag']}"
    + 首次进入 tag 后,Modrinth/CurseForge 的 changelog 链接依赖服务端把路径里的 + 当字面量。建议 quote(plan['tag'], safe=''),或直接复用 release 对象的 html_url(内部已有该字段)。
  3. 双空行:release.py:88 在 notes 后固定追加 "",若 GitHub 返回内容本身以换行结尾,说明与校验表之间会出现两个空行(仅外观)。
  4. 封存后无法再开启 notes:seal() 复用草稿时不刷新 body,而 prepare() 有「tag 属于哪个 run」检查 ⇒ 首次 seal 完成后,该版本只能删 tag/release 重来才能补生成说明。建议在 RELEASING.md 写一句,或让 seal() 在 existing 且 plan["release_notes"] 非空时对 body 做 PATCH。

验证边界

脚本从 head 抽出后在本地 Python 3.11.2 实跑(unittest discover),未联网、未调用 GitHub API;工作流 YAML 仅做静态阅读,未在 Actions 中实际执行。PR 描述中"用实际 GitHub 接口预览 v2.0.0+beta.1"与"2.0.0+rc.1 实际 stageRelease 构建 22 组坐标"两项我无法独立复核,请以作者提供的运行记录为准;generate-notes 的 previous-tag 行为建议由作者补一次连续两版实测。

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.

2 participants