- 桌面应用
- 开发工具
【免费下载链接】MooTool
Handy tool set for developers
MooTool 是一个"同一仓库、多个独立产品线"的 monorepo:Java 版、Electron 版、Tauri 版与原生 macOS 版各自维护版本号、发布节奏和更新通道。本文基于仓库根目录的发布约定文档 RELEASE_CONVENTIONS.md,结合update-manifest.json、GitHub Actions 工作流与发布校验脚本scripts/manage_releases.py的实际实现,完整讲解 MooTool 的多产品版本隔离方案——读完你可以理解:Git tag 前缀如何充当"产品边界"、全局Latest为什么只能属于一个产品、CI 如何用一份 Python 脚本做跨产品的版本一致性校验,以及客户端更新检查为什么只读取自己产品节点的清单。
1. 为什么需要这份约定
MooTool 仓库里同时存在多条产品线:
| 产品目录 | 技术栈 | 状态(以仓库现状为准) |
|---|---|---|
根目录src/ | Java(Swing,pom.xml当前版本 1.8.7) | 历史产品,持续维护 |
| next/ | Electron + Vite + React + TypeScript(next/package.json当前版本 1.3.2) | 当前主力产品 |
| next-tauri/ | Tauri 2 + Rust + React | Release candidate(0.1.0-rc 系列) |
| next-macos-native/ | Swift 原生(VERSION文件当前为 0.8.0) | 公开预发布 |
这些产品版本号互相独立:Java 的1.8.7、Electron 的1.3.2、原生 macOS 的0.8.0可以在同一仓库中并存,互不干扰。但这带来一个工程问题:Git tag 在整个仓库内是全局命名空间,v1.0.0只能指向一个提交。约定文档给出的答案是——用 tag 前缀做产品隔离,并且"产品线由 tag、Release、安装包名称、应用 ID 和更新清单共同隔离,不依赖长期分叉的产品分支"。
文档第 1 节确立了五条基本原则:
- Java、Electron 及未来的 Tauri、原生 macOS 实现是不同产品线,各自维护版本号和发布节奏;
- 产品版本遵循语义化版本(SemVer),例如
1.0.0、1.1.0、2.0.0-beta.1;不同产品可以同时拥有相同版本号; - Git tag 在仓库内必须唯一,因此除历史 Java 产品线外,tag 必须带稳定的产品 ID 前缀;
- 产品线由 tag、Release、安装包名称、应用 ID 和更新清单共同隔离;
- 已发布的 tag 和 Release 不得改指向其他提交,也不得被另一个产品复用。
2. 产品与 tag 命名规则
约定文档第 2 节定义了核心映射表,产品 ID、版本来源与 tag 前缀一一对应:
| 产品 ID | 产品 | 版本来源 | Git tag | Release 标题示例 |
|---|---|---|---|---|
java | MooTool Java | pom.xml、UiConsts.APP_VERSION、version_summary.json | v{version} | MooTool Java 1.7.10 |
next-electron | MooTool Next Electron | next/package.json | next-electron-v{version} | MooTool Next Electron 1.0.0 |
next-tauri | MooTool Next Tauri | 启用产品时确定 | next-tauri-v{version} | MooTool Next Tauri 1.0.0 |
next-macos-native | MooTool Next macOS Native | next-macos-native/VERSION | next-macos-native-v{version} | MooTool Next macOS Native 1.0.0 |
tag 命名示例:
v1.7.9 # Java 1.7.9 v1.8.0 # Java 1.8.0 next-electron-v1.0.0 # Electron 1.0.0 next-electron-v1.1.0 # Electron 1.1.0 next-electron-v2.0.0-beta.1 # Electron 2.0.0 beta 1 next-tauri-v1.0.0 # Tauri 1.0.0为什么 Java 保留无前缀的v*?文档明确解释:历史 Java tagv1.0.0至v1.7.9保持不变,v*命名空间保留给 Java,新产品不得使用无产品前缀的 tag;除非单独评审和迁移,否则不把 Java 改为java-v*,以免破坏历史连续性和现有自动化。这一点在仓库中可以验证——src/main/resources/version_summary.json的versionIndex里完整保留了从v1.0.0到v1.8.7的连续历史版本序列,任何改名都会让历史客户端的版本索引失效。
版本来源在源码中的落点
约定中的"版本来源"不是抽象说法,每个来源都对应仓库中的具体文件,且会被 CI 逐一核对:
- Java 产品:pom.xml 的
<version>(当前1.8.7)、UiConsts.java 中的APP_VERSION = "v1.8.7"(供关于对话框展示),以及 version_summary.json 的currentVersion字段; - Electron 产品:next/package.json 的
version字段(当前1.3.2); - 原生 macOS 产品:next-macos-native/VERSION 单行文件(当前
0.8.0)。
3. GitHub Release 约定
每个 tag 对应一个且仅一个产品版本的 GitHub Release。文档第 3 节的规则逐条如下:
- Release 标题必须包含完整产品名和版本号,不能只写
v1.0.0; - Release 正文开头必须说明产品线,并注明它是否替代其他实现;
- Release 只上传当前产品的安装包和更新元数据,不混入其他产品的产物;
- 安装包文件名必须包含产品名、版本、操作系统和架构;同一平台存在多种包型时还必须包含包型(Electron 的 Linux 产物同时有
AppImage与deb两种包型,文件名以packageType区分); - GitHub 自动生成的源码压缩包包含整个仓库,这是 monorepo 的预期行为,面向普通用户的下载入口应指向 Release 安装包而非源码包;
- Release Notes 只能比较同一产品线的前后两个 tag,不能把另一产品的最近 tag 当作上一个版本——这是 monorepo 下使用 GitHub 默认"compare 自动生成 changelog"功能最容易踩的坑;
- 版本说明有唯一的人工维护来源:Electron 以
next/release-notes/{version}.md为准(当前仓库中已有1.0.0.md至1.3.2.md的完整序列),GitHub Release 和update-manifest.json均从该文件生成;Java 以src/main/resources/version_summary.json为准,GitHub Release 从当前版本节点生成; - 三语要求:Release 正文必须依次包含 English、中文、日本語三部分。Java 当前版本节点必须提供
title/log、titleEn/logEn、titleJa/logJa六个字段;Electron 的版本 Markdown 必须直接维护完整三语内容。
文档建议在 Release 正文顶部使用这样的说明:
> 产品线:MooTool Next Electron > 版本:1.0.0 > 本版本与 MooTool Java 独立安装、独立更新,不会覆盖 Java 版本。源码级佐证:三语与版本一致性不是"建议",而是 CI 硬校验
这些规则由 scripts/manage_releases.py 在 CI 中以代码形式强制实施:
validate_multilingual_release_notes会检查 Release notes 中## English、## 中文、## 日本語三个标题存在且顺序正确,否则直接抛错终止流水线;electron_release_info要求next/package.json的版本拼出的next-electron-v{version}与 tag 完全一致,且next/release-notes/{version}.md的第一行必须是# MooTool Next Electron {version}、正文不超过 5000 字符;java_release_info则做四重校验:pom.xml版本、UiConsts.APP_VERSION值、version_summary.json的currentVersion与versionIndex条目、以及六个三语字段全部非空——任何一处不一致都会导致 Java 发布失败;- 由于 Java 的历史更新协议没有预发布通道,脚本对 Java 版本显式拒绝 SemVer 预发布号(
MooTool Java prereleases are not supported by the legacy stable update feed); - 脚本生成的 Java Release 正文会自动附加一行产品边界声明:"This release updates MooTool Java only; it does not replace or upgrade MooTool Next Electron."(中文、日本語同理),从机制上保证"注明是否替代其他实现"这条规则不会遗漏。
4.Latest与下载入口
GitHub 仓库只有一个全局LatestRelease,无法设置"Latest Java"和"Latest Electron"并存。文档第 4 节的解决方案:
Latest表示当前推荐给新用户的主力稳定产品,而不是所有产品中数值最大的版本;- 当前主力产品为 MooTool Next Electron,稳定的 Electron Release 可以设为
Latest; - Java 及其他非主力产品发布时不得覆盖全局
Latest,自动化中应显式使用等价于make_latest: false的设置; - beta、rc 等测试版本必须标记为 pre-release,不得设为
Latest; - README 和官网应分别提供各产品的"最新版本"入口,不以仓库级
/releases/latest代替产品更新清单。
这条约定在 CI 中有精确的执行点。build-installers.yml(Java 发布流水线)在发布 Release 时硬编码了make_latest: false——也就是说 Java 版本无论发什么版本,都不会抢占全局Latest;而 next-build-installers.yml(Electron 流水线)由manage_releases.py输出make_latest:稳定版为true,预发布版为false。两者组合后,全局Latest事实上只会被 Electron 稳定版占据,与文档中"当前主力产品"的定义一致。
从源码结构看,客户端侧也不依赖这个全局Latest:Electron 的 updateService.ts 中,parseManifest只从update-manifest.json的products[productId]节点取数据,并强制要求schemaVersion === 1且产品status为active;版本比较用内置的 SemVercompareVersions完成,而不是拉取 GitHub 的Latest接口。
此外文档还规定:Java 发布工具只接受稳定版v{major}.{minor}.{patch},需要测试 Java 安装包时使用手动 CI 工件(workflow_dispatch 的release_tag输入项即为此设计),不创建会被稳定客户端发现的预发布版本;Electron 则支持 SemVer 预发布,并由客户端自动隔离稳定与预发布更新。若未来主力实现发生变化,应先修改本文件和下载入口,再调整哪个产品可以更新全局Latest。
5. 分支策略:产品线不是分支线
文档第 5 节的立场很明确——默认分支同时保存所有产品目录、公共资源、文档和 CI 配置:
- 日常开发使用当前主干与短期功能分支;
- 需要维护旧版本时可创建短期或维护型分支(例如
release/java-1.x),但发布身份仍由产品 tag 决定,不由分支决定; - 不为 Java 和 Electron 维护永久互不合并的两套主分支,避免公共文档、资源和自动化长期漂移;
- 同一提交可以被不同产品的 tag 引用,但每个 tag 只发布对应产品的产物。
这一策略的直接收益体现在发布脚本与 CI 配置这类"公共资产"上:scripts/manage_releases.py、scripts/generate_download_links.py被 Java 和 Electron 两条流水线共同调用,README 中的产品对照表(链接到本约定文档)也只需要一份。如果按产品拆分成互不合并的主分支,这些公共逻辑就会复制出多份并各自漂移。
6. CI 隔离与校验:tag 前缀即产品边界
发布工作流必须通过 tag 前缀触发,而不是根据整个仓库的最高版本判断产品。文档给出的触发规则表与当前.github/workflows/目录下的实际文件一一对应:
| 工作流 | tag 触发规则 | 发布内容 |
|---|---|---|
| .github/workflows/build-installers.yml | v* | MooTool Java |
| .github/workflows/next-build-installers.yml | next-electron-v* | MooTool Next Electron |
| .github/workflows/next-macos-native-build.yml | next-macos-native-v* | MooTool Next macOS Native(Universal DMG Pre-release,make_latest: false) |
仓库中实际还存在next-tauri-ci.yml、next-tauri-release.yml、next-tauri-promote-update.yml等 Tauri 产品的工作流,遵循同样的next-tauri-v*前缀隔离思路(约定文档写于 Tauri 产品"启用产品时确定版本来源"之前,具体触发规则以对应工作流文件为准)。
每条发布流水线至少完成六步校验:
- 从 tag 中解析产品 ID 和版本号;
- 校验 tag 版本与该产品的版本来源完全一致;
- 只构建、测试和收集当前产品目录的产物;
- 校验产物文件名、操作系统、架构和包型没有冲突或缺失;
- 创建或更新当前 tag 对应的 Release,并按约定处理 pre-release 和
Latest; - 从当前产品的版本说明来源生成 Release Notes,不调用可能跨产品比较 tag 的默认自动说明。
普通提交或 Pull Request 可以使用目录过滤(例如next-macos-native-build.yml对next-macos-native/**路径做了paths过滤以减少无关构建),但正式发布必须以 tag 前缀作为最终产品边界。
以 Electron 流水线为例,其preparejob 的执行顺序完整体现了上述校验链:npm ci→npm run check(跑测试)→ 调用python -u scripts/manage_releases.py validate-electron --tag ...(校验 tag 与next/package.json一致性并产出version/title/prerelease/make_latest等输出)→ 再额外执行python -m unittest scripts.test_manage_releases对发布工具自身做回归测试。随后矩阵构建 macOS Apple Silicon / macOS Intel / Windows x64 / Linux x64 四个平台产物,Java 流水线则额外在发布前用manage_releases.py validate-java渲染三语 Release Notes,并在资产就绪后回写download_links.json到 master 分支。
7. 更新通道隔离
这是整套约定中"让用户感知不到 monorepo"的关键层。文档第 7 节的隔离规则:
- Java继续使用
version_summary.json和download_links.json,保证历史客户端兼容; - Electron使用根目录 update-manifest.json 中的
next-electron节点; - 未来产品必须在
update-manifest.json使用自己的产品 ID,不得读取或写入其他产品节点; - 各客户端只能比较自身产品节点中的版本,不能用 GitHub 仓库的全局
Latest判断更新; - 应用 ID、产品名和用户数据目录也应按产品隔离,保证不同实现可以同时安装(Electron 客户端使用独立应用 ID
com.rememberber.mootool.next.electron,详见 next/doc/update-products-and-assets.md); - Electron 稳定版客户端忽略
prerelease版本;beta/rc 客户端可以继续接收后续预发布版本,并在正式版发布后升级到正式版; - Java 使用
versionIndex判断版本先后及汇总中间版本说明,不使用字符串比较版本号。
update-manifest.json的实际结构印证了这套设计:顶层是schemaVersion: 1加一个products注册表,其中java节点为status: "legacy"且只保留指向version_summary.json的updateFeedUrl(供旧协议兼容),next-electron为status: "active"并维护完整的releases数组(当前含 1.0.0 起 27 个稳定版本,每条带prerelease标志、releaseUrl与按平台/架构/包型组织的assets,每个资产带sha512与size),next-tauri节点已有两个 rc 预发布版本(0.1.0-rc.1、0.1.0-rc.2),next-macos-native节点状态为 planned。
客户端侧的隔离逻辑可以在 updateService.ts 中逐行验证:parseManifest会拒绝未注册、非 active 或版本重复的产品节点;版本筛选逻辑为allowPrerelease = 当前版本含 prerelease 段 ? 允许 : 只接受稳定版,再在合格版本中取 SemVer 最大者,与当前版本比较后才提示更新——这正是文档所述"客户端自动隔离稳定与预发布更新"的实现。
Electron 的清单格式、资产选择和自动下载细节见 next/doc/update-products-and-assets.md,该文档开头也明确声明:仓库级版本号、Git tag、Release、Latest和 CI 隔离规则以本约定文档为准,它只补充客户端更新清单与安装包选择的实现细节。
8. 发布顺序
正式发布按以下顺序执行(文档第 8 节):
- 更新目标产品的版本来源、变更说明和必要文档,并完成测试;
- 创建符合命名规则的 tag,CI 校验版本一致性;
- 构建安装包和自动更新元数据,由流水线核对平台、架构、签名状态、文件名和更新元数据;
- 所有目标校验通过后发布 Release,并按产品角色设置 pre-release 和
Latest; - Release 资产可访问后,再更新并启用该产品的更新清单;
- 验证 README 下载入口、客户端更新检查和安装流程。
顺序中有一条安全红线:不要在 Release 资产尚未可用时提前向已发布客户端暴露新版本,以免客户端发现版本后无法下载。这对更新清单类产品尤其重要——update-manifest.json中一旦出现某版本的releases条目,所有该产品的客户端在下次检查时就会认为"存在新版本",如果对应的 Release 资产不可用,更新流程将直接指向 404。
关于签名,文档明确:当前 Electron 产品线允许未签名发布,流水线必须标注这一状态但不得因缺少证书而阻止 Release。这与 CI 实现一致——next-build-installers.yml中有一个Report unsigned release mode步骤,推送 tag 触发时输出 warning:"This workflow intentionally publishes unsigned Electron packages. macOS updates download a verified DMG in the background, then require manual installation." 未签名 macOS 客户端可以在后台下载并校验 DMG,完成后提示用户打开 DMG 并按常规方式拖入"应用程序",但不得调用应用内自动安装;具体限制见 next/doc/update-products-and-assets.md 的"未签名发布策略"一节。
9. Electron 首个正式版本:两个1.0.0如何共存
文档第 9 节对 Electron 产品的起始版本做了显式钉死:
应用版本:1.0.0 Git tag:next-electron-v1.0.0 Release 标题:MooTool Next Electron 1.0.0该版本不占用、不删除、不重建历史 Java tagv1.0.0,也不表示 Java 版本升级为 2.0.0——两个1.0.0属于不同产品线,可以在同一仓库中独立存在。这条规则回答了多产品 monorepo 最常见的疑问"新产品的 1.0.0 和老产品的 1.0.0 会不会冲突":不会,因为二者的 tag(next-electron-v1.0.0vsv1.0.0)、Release 标题、安装包文件名、应用 ID 和更新清单节点全部不同;update-manifest.json中next-electron的 releases 数组也确实以1.0.0为第一个稳定版本起步。
10. 小结:约定文档 + 清单文件 + CI 脚本的三层闭环
回顾整份 RELEASE_CONVENTIONS.md,它的可执行性来自三层机制的互相咬合:
- 命名层:tag 前缀 = 产品 ID,产品 ID = 更新清单节点键、应用 ID 前缀、安装包命名前缀,一个标识贯穿所有发布面;
- 校验层:scripts/manage_releases.py 把"tag 与版本来源一致""三语 notes 完整""预发布不得抢 Latest"等规则从文档约定升级为 CI 失败条件;
- 消费层:客户端(如 updateService.ts)与清单文件(update-manifest.json)只认自己的产品节点,Java 历史客户端继续走
version_summary.json+download_links.json旧协议。
对维护者而言,日常只需记住三步:改自己产品的版本来源文件、按前缀规则打 tag、让 CI 完成其余校验;对扩展者而言,接入新产品线的模板也很清晰——新增next-xxx-v*tag 前缀、专属工作流、update-manifest.json产品节点和独立应用 ID,即可在不触碰其他产品的前提下加入这套多产品发布体系。
- 桌面应用
- 开发工具
【免费下载链接】MooTool
Handy tool set for developers
相关推荐
GenOffice架构解析:6个Electron应用如何共享一套引擎层(Monorepo完整指南)
GenOffice架构解析:6个Electron应用如何共享一套引擎层(Monorepo完整指南) GenOffice 是一个免费开源的 AI 办公套件,支持编
人工智能AI 应用桌面应用AI AgentMCP 服务AI 技能shadPS4 0.7.0 一启动就崩?这份 Windows 11 排障指南帮你 4 步救回 PS4 游戏
shadPS4 0.7.0 一启动就崩?这份 Windows 11 排障指南帮你 4 步救回 PS4 游戏 昨天例行升级,我把 shadPS4 刷到了最新的 0
虚拟化图形学终极指南:如何使用Mise在同一台机器上管理多个Tuist版本
终极指南:如何使用Mise在同一台机器上管理多个Tuist版本 在iOS开发过程中,不同项目可能需要不同版本的Tuist工具来确保兼容性和稳定性。Tuist作为
开发工具CLI后端云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考