news 2026/10/23 14:02:17

MooTool 多产品发布约定:一个 monorepo 里 Java、Electron 与 Tauri 如何共用同一套版本号体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MooTool 多产品发布约定:一个 monorepo 里 Java、Electron 与 Tauri 如何共用同一套版本号体系
  • 桌面应用
  • 开发工具

【免费下载链接】MooTool

Handy tool set for developers

项目地址:https://gitcode.com/gh_mirrors/mo/MooTool
点击查看免费下载

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 + ReactRelease 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 节确立了五条基本原则:

  1. Java、Electron 及未来的 Tauri、原生 macOS 实现是不同产品线,各自维护版本号和发布节奏;
  2. 产品版本遵循语义化版本(SemVer),例如1.0.0、1.1.0、2.0.0-beta.1;不同产品可以同时拥有相同版本号;
  3. Git tag 在仓库内必须唯一,因此除历史 Java 产品线外,tag 必须带稳定的产品 ID 前缀;
  4. 产品线由 tag、Release、安装包名称、应用 ID 和更新清单共同隔离;
  5. 已发布的 tag 和 Release 不得改指向其他提交,也不得被另一个产品复用。

2. 产品与 tag 命名规则

约定文档第 2 节定义了核心映射表,产品 ID、版本来源与 tag 前缀一一对应:

产品 ID产品版本来源Git tagRelease 标题示例
javaMooTool Javapom.xml、UiConsts.APP_VERSION、version_summary.jsonv{version}MooTool Java 1.7.10
next-electronMooTool Next Electronnext/package.jsonnext-electron-v{version}MooTool Next Electron 1.0.0
next-tauriMooTool Next Tauri启用产品时确定next-tauri-v{version}MooTool Next Tauri 1.0.0
next-macos-nativeMooTool Next macOS Nativenext-macos-native/VERSIONnext-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 节的解决方案:

  1. Latest表示当前推荐给新用户的主力稳定产品,而不是所有产品中数值最大的版本;
  2. 当前主力产品为 MooTool Next Electron,稳定的 Electron Release 可以设为Latest;
  3. Java 及其他非主力产品发布时不得覆盖全局Latest,自动化中应显式使用等价于make_latest: false的设置;
  4. beta、rc 等测试版本必须标记为 pre-release,不得设为Latest;
  5. 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.ymlv*MooTool Java
.github/workflows/next-build-installers.ymlnext-electron-v*MooTool Next Electron
.github/workflows/next-macos-native-build.ymlnext-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 产品"启用产品时确定版本来源"之前,具体触发规则以对应工作流文件为准)。

每条发布流水线至少完成六步校验:

  1. 从 tag 中解析产品 ID 和版本号;
  2. 校验 tag 版本与该产品的版本来源完全一致;
  3. 只构建、测试和收集当前产品目录的产物;
  4. 校验产物文件名、操作系统、架构和包型没有冲突或缺失;
  5. 创建或更新当前 tag 对应的 Release,并按约定处理 pre-release 和Latest;
  6. 从当前产品的版本说明来源生成 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 客户端使用独立应用 IDcom.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 节):

  1. 更新目标产品的版本来源、变更说明和必要文档,并完成测试;
  2. 创建符合命名规则的 tag,CI 校验版本一致性;
  3. 构建安装包和自动更新元数据,由流水线核对平台、架构、签名状态、文件名和更新元数据;
  4. 所有目标校验通过后发布 Release,并按产品角色设置 pre-release 和Latest;
  5. Release 资产可访问后,再更新并启用该产品的更新清单;
  6. 验证 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,它的可执行性来自三层机制的互相咬合:

  1. 命名层:tag 前缀 = 产品 ID,产品 ID = 更新清单节点键、应用 ID 前缀、安装包命名前缀,一个标识贯穿所有发布面;
  2. 校验层:scripts/manage_releases.py 把"tag 与版本来源一致""三语 notes 完整""预发布不得抢 Latest"等规则从文档约定升级为 CI 失败条件;
  3. 消费层:客户端(如 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

项目地址:https://gitcode.com/gh_mirrors/mo/MooTool
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 15:29:21

OceanBase 2.2数据库性能测试实战:JMeter分布式事务压测指南

1. 项目概述&#xff1a;为什么一个数据库性能测试值得专门写一篇实操笔记&#xff1f;OceanBase 2.2 这个版本在业内有个很实在的标签——“从金融核心场景反向锤炼出来的稳定器”。它不是靠参数堆出来的纸面性能&#xff0c;而是被某大型支付平台的真实交易洪峰反复冲刷过、在…

作者头像 李华
网站建设 2026/10/18 9:30:42

gifsicle预构建检测失败的根因与工程化解决方案

1. 项目概述&#xff1a;当构建流程在 gifsicle 上突然卡住“‼ gifsicle pre-build test failed”——这个报错信息&#xff0c;我第一次在 CI/CD 流水线里看到时&#xff0c;正赶在凌晨两点打包一个即将上线的营销活动页。页面里嵌了 7 个动态加载的 GIF 图片&#xff0c;用的…

作者头像 李华
网站建设 2026/10/19 6:57:22

ROC曲线与AUC:模型排序能力的诊断听诊器

1. 这不是数学考试&#xff0c;是模型诊断的听诊器你刚跑完一个二分类模型&#xff0c;训练集准确率98%&#xff0c;测试集也到了92%——看起来很美。但上线后业务方打来电话&#xff1a;“为什么把30%的高风险客户标成了低风险&#xff1f;漏掉的这批人&#xff0c;坏账率直接…

作者头像 李华