Skip to content

ci(release): 使用手动 tag 覆盖七分支发布版本 - #255

Merged
PigeonNian merged 1 commit into
Anvil-Dev:dev/26.1from
Gu-ZT:multi-version/26.1
Nov 11, 2026
Merged

PigeonNian merged 1 commit into
Anvil-Dev:dev/26.1from
Gu-ZT:multi-version/26.1

Conversation

@Gu-ZT

@Gu-ZT Gu-ZT commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Create Multi-Version Releases 现在要求输入不带 v 前缀的 tag,直接以它作为所有 Minecraft 构建的版本。例如输入 2.0.0+pre-release.13,Git tag、JAR 内版本和 Maven 版本均为该值,不再从分支的 gradle.properties 读取发布版本,也不要求开发版本一致。

  • 构建和 JReleaser 部署均显式传入 -Pmod_version,构件校验使用输入版本。
  • 版本后缀继续决定 GitHub / 模组平台类型;纯 X.Y.Z 才创建下一 patch 的 PR,并比较锁定源码中的原始开发版本防止覆盖并发修改。
  • 重跑使用冻结的发布计划并拒绝改换 tag,普通开发 CI 保持 Beta。

验证:27 项 Python 发布测试通过,release.yml 的 actionlint 和 git diff --check 通过。实际执行 CI_BUILD=false ./gradlew -PanvillibRelease -Pmod_version=2.0.0+pre-release.13 stageRelease --offline --no-configuration-cache 成功,artifacts.py 校验通过 22 个 Maven 发布单元和 21 个内嵌模块;gradle.properties 未改写。未执行真实发布。

- 必填无 v 前缀的 tag 并覆盖构建与 Central 部署版本
- 按输入后缀决定发布类型和升版规则,保持 Beta 开发快照
- 校验重跑 tag 一致性,并从锁定开发版本安全创建升版 PR

@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.

结论:核心机制已验证成立——本地实跑 27 项发布测试全通过,-Pmod_version 已覆盖构建与 JReleaser 部署两条路径,且手工 tag 的校验链完整;但 bump() 缺少“版本是否已更新”的判断,存在把分支版本写回旧值的路径,且预检不再校验 mod_version 的可解析性,这一点只有到发布成功后的 bump 才会暴露。

审查范围:base dev/26.1 → head e73df50a,8 文件 +96/−48(patch-diff 与 API 声明逐位一致)、draft=false、mergeable=true、无既有 review。前置 #252 已于 2026-11-10T07:28Z 合并,默认分支上已有 Create Multi-Version Releases 与 release_notes.py。

✅ 一、已验证成立的部分

27 项测试本地实跑通过(抽出 head 的 .github/release/ 后 unittest discover):Ran 27 tests in 0.130s — OK。

检查点 结论
手工 tag 作为唯一版本源 prepare() 用 RELEASE_TAG,plan = {"tag": tag, "version": tag};删除了七线 mod_version 一致性校验,与文档一致
tag 校验时机 release_type(tag) 在 artifact() 之前;测试断言非法 tag(空、v2.0.0、2.0、2.0.0-beta.1、2.0.0+snapshot.5、含换行、$(example))时 artifact/github 均未被调用
v 前缀清除彻底 全仓 grep 无残留 "v" + 构造;TAG/PLAN/publish/bump/断言全部改为无前缀;tag_manifest 改 re.fullmatch(VERSION, tag)、manifest["version"] == tag、asset stem 用 tag
-Pmod_version 双通道 release.yml:91 构建传入;central.py 的 jreleaserDeploy 命令行传入(有专门测试断言 -Pmod_version=<version> 在 argv 中)
构件校验用输入版本 artifacts.py 以 version 校验 POM 坐标、内部依赖 version+scope=compile、主 JAR 内 neoforge.mods.toml 的 version、jarjar 内嵌 artifactVersion、slim JAR 的 mod 描述符
重跑防篡改 已保存计划时 require(plan["tag"] == tag, "Input tag differs…"),测试验证此时不发任何 API
bump 的漂移防护 expected 取自锁定源码、current 取自 base HEAD,不等即报 expected … from the locked source
文档对齐 RELEASING.md 同步更新了表格标题(输入 tag)、-Pmod_version 说明、状态名去 v(release/2.0.0/26.1/central)、patch 计算口径

⚠️ 二、bump() 缺少“已经更新”判断,存在版本写回路径

major, minor, patch = version.split(".")
next_version = f"{major}.{minor}.{int(patch) + 1}"
current = mod_version(props)
if current == next_version:                      # ← 只挡"恰好相等"
    summary(f"{base} is already at {next_version}."); return
expected = mod_version(contents("gradle.properties", plan["targets"][line]["sha"]))
require(current == expected, …)                  # ← 只挡"分支被人改过"

next_version 由 tag 的 core 计算,current 是分支的开发版本,两者没有大小关系检查。若分支已处于更新的序列(例如 2.1.1),而本次补发一个低序列 tag(例如 2.0.5),则 current(2.1.1) != next_version(2.0.6)、且 current == expected 成立 → 会把 gradle.properties 从 2.1.1 改写成 2.0.6(版本回退),并创建一个标题为「将版本推进到 2.0.6」的 PR。

RELEASING.md 写的是「仅当根版本仍等于锁定源码中的开发版本时才修改……其他版本报告冲突」,但此路径既不冲突也不跳过。

建议:解析 core 三元组后比较,current_core >= next_version 时记为“无需修改”并跳过写回(或明确报冲突)。改动很小、测试也容易补一条。

⚠️ 三、预检不再校验 mod_version,失败点被推迟到发布之后

prepare() 现在只读 java_version 与 gradle/scripts/release.gradle,mod_version() 仅剩 bump() 使用:

props = contents("gradle.properties", sha)
java = re.search(rb"(?m)^java_version=(\d+)\s*$", props).group(1).decode()
contents("gradle/scripts/release.gradle", sha)      # ← 不再读 mod_version

由于构建由 -Pmod_version 供给版本,某条分支的 gradle.properties 即使缺少 mod_version 或出现多行,预检与七个构建都会通过;直到发布已经推到 Central / GitHub 之后,bump() 里的 mod_version() 才抛 Expected one mod_version property,bump job 失败。

建议:预检保留“能解析出唯一 mod_version”的检查(只校验可解析,不比较七线是否一致、不比较是否等于 tag),把失败点前移到 dispatch 的第一分钟。这与本 PR 的设计并不冲突。

💡 四、其余建议

  1. bump() 的等值早退在漂移检查之前:if current == next_version: return 先于 require(current == expected),因此“分支已是目标版本、但锁定源码版本不同”会被当作完成。影响很小,但与文档「其他版本报告冲突」的措辞略偏,可把漂移检查上移。
  2. tag 在部分 GitHub 路径未做 URL 编码:releases/tags/{tag}、git/ref/tags/{tag} 直接拼 tag,而同仓 release_notes.py 的 compare/{quote(tag, safe='')} 是编码的。+ 在路径中按 RFC 3986 属合法字面量、实际大概率可用,但同一代码库两套写法建议统一(mod_sites.release_url() 同理,…/releases/tag/{plan['tag']})。这是既有代码,本次 tag 只是去掉了 v。
  3. 迁移期提示:新的 tag_manifest 只接受无 v 的 tag。若切换时存在 vX.Y.Z 的在途草稿,升级后无法续跑(历史已发布 tag 不受影响——generate_notes 只读 tag_name,仍可作为 previous_tag_name 候选)。建议在 RELEASING.md 加一句「切换前的在途发布需先收尾或放弃」。

验证边界

脚本从 head 抽取后在本地 Python 3.11.2 实跑(27 项全过),未执行任何真实发布、未调用 GitHub API;release.yml 仅静态阅读,未跑 actionlint(PR 描述称已通过)。PR 描述中「实际 stageRelease 构建 + artifacts.py 校验 22 组坐标 / 21 个内嵌模块」我无法独立复核,请以作者运行记录为准;gradle.properties 未被改写这一点与代码一致(构建只用 -Pmod_version,prepare 不写文件)。

@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 ⚪ Skipped —
wheel ✅ Compatible 0
main ✅ Compatible 0

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

@PigeonNian
PigeonNian merged commit 916b71a into Anvil-Dev:dev/26.1 Nov 11, 2026
1 check passed
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