EMQX 开源仓库贡献指南:分支同步链、Conventional Commit 规范与 Changelog 工程实践
【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx
本文以 CONTRIBUTING.md 为骨架,结合 EMQX 仓库中真实的脚本(如 scripts/check-changes-filename-pattern.sh、scripts/ff-release-from-dev.sh)与
changes/目录的既有产物,系统讲解向 EMQX 提交代码时必须遵守的三件事:往哪个分支提 PR、如何书写规范的 commit message、以及如何登记 changelog。读完本文,你将能准确判断 PR 的目标分支,写出符合仓库要求的提交信息,并为自己的改动添加一条能通过 CI 校验的变更记录。
一、先了解仓库:EMQX 的提交入口
EMQX 是一个以 Erlang/Elixir 为主实现的高性能 MQTT 消息服务器,仓库根目录采用 monorepo 结构,核心业务代码集中在apps/目录下(例如apps/emqx、apps/emqx_auth、apps/emqx_bridge_kafka等),发布相关脚本位于scripts/,版本演进记录位于changes/。向这样一个多版本并行维护的仓库提交代码,分支策略与提交规范是合入的第一步关卡。
本仓库欢迎任何形式的 Bug 报告、Issue 与功能请求(feature request)。在动手提交代码之前,建议先通读本指南,重点理解以下三个环节:
- 分支目标选择——决定你的改动最终进入哪些发布线(release line);
- 提交信息格式——决定你的提交在
git log与自动化工具中的可读性; - Changelog 登记——决定你的改动是否会被收录进官方版本变更记录。
二、分支策略:选择正确的目标分支
2.1 dev-XX 分支与正向同步链(forward-sync chain)
EMQX 同时维护多条dev-XX分支,每条对应一个仍在支持的发布线(例如dev-58、dev-60、dev-63)。这些分支之间存在一条自动正向同步链:一个改动合入较早的分支后,会通过同步机制自动传递到链条上的每一个后续分支,无需为每条分支分别提交 PR。
从仓库脚本 scripts/rel-versions 可以确认当前 6.x 系列的同步链形态:
dev-60 -> dev-61 -> dev-62 -> dev-63 -> dev-70该脚本注释明确指出:"The 6.x line ends at dev-63; there is no 6.4 release, so dev-63 syncs forward into dev-70"(6.x 线止步于 dev-63,由于不存在 6.4 版本,dev-63 继续向前同步到 dev-70)。这说明同步链可以跨大版本延续。该脚本还说明了链上各分支与发布线的关系:release-XX分支会从对应的dev-XX分支自动快进(见 2.3 节),因此合入 dev 分支的改动最终会到达该发布线的发布分支。
2.2 目标分支的选择规则
- 优先选择改动影响的最早的仍活跃
dev-XX分支,尤其是高严重级别(high-severity)的修复。因为同步链是单向向前的,只有合入最早的受影响分支,修复才能经由链条覆盖所有下游发布线。 - 如果不确定哪条分支最早或仍活跃,直接瞄准最新的
dev-XX分支即可。此时应在 PR 描述中说明这一情况并请求维护者指引;评审过程中维护者可以视情况将 PR 重定向到更早的分支。 - 切勿针对多条
dev-XX分支提交重复 PR。同步链本身会向前传播改动,重复 PR 只会造成重复评审工作量,且若未同时合入还会导致内容分叉(diverge)。 - 绝不直接向
release-XX分支提交 PR。这些分支由对应的dev-XX分支自动快进而来,不接受直接推送。
2.3 发布分支的快进机制(源码佐证)
release-XX不接受直接推送这一规则,在脚本 scripts/ff-release-from-dev.sh 中有完整的落地实现。该脚本用于将release-XX快进(fast-forward)到dev-XX,核心逻辑是:
VERSION="$1" DEV_BRANCH="dev-${VERSION}" RELEASE_BRANCH="release-${VERSION}" # 若两者 SHA 相同则无事可做 if [ "${DEV_SHA}" = "${REL_SHA}" ]; then exit 0 fi # git push 不带 --force,本质上就是仅快进操作 git push origin "${DEV_SHA}:refs/heads/${RELEASE_BRANCH}"脚本刻意依赖git push(不带--force)天然拒绝非快进更新的特性来保证release-XX始终是dev-XX的祖先;当出现分叉时,会在 CI 环境变量中写入NOT_FAST_FORWARD=1并报错。这一设计从工程层面印证了 CONTRIBUTING.md 中"release 分支仅由 dev 分支快进、不接受直接推送"的约束。
三、Commit Message 规范
仓库对 commit message 有非常精确的格式要求,目的是让项目历史更易阅读、更易被 git 工具与自动化流程消费。
3.1 整体格式
每条提交信息由header、body与footer三部分组成,header 内部又分为type、scope与subject:
<type>(<scope>): <subject> <BLANK LINE> <body> <BLANK LINE> <footer>- header(含 type)是必填项,header 中的scope 为可选项;
- 本仓库没有预定义 scope 列表,允许按需自定义 scope 以提升可读性;
- 每行长度不得超过 100 个字符,以保证在 GitHub 界面和各种 git 工具中都能被完整阅读;
- footer 中如有关联的 Issue,应写入关闭引用(closing reference),例如
Closes: #123。
3.2 完整示例
示例 1:仅含 header 的最小提交
feat: add Fuji release compose files示例 2:带 scope、body 与 footer 的完整提交
fix(script): correct run script to use the right ports Previously device services used wrong port numbers. This commit fixes the port numbers to use the latest port numbers. Closes: #123, #245, #992可以看到,body 中先用一句话说明"之前的错误行为",再说明"本次提交做了什么"——这正是规范要求的"说明动机并与旧行为做对比"。
3.3 字段细则
Revert(回滚提交):如果提交是对某次历史提交的回滚,必须以revert:开头,随后跟被回滚提交的 header,并在 body 中写明This reverts commit <hash>.(hash 为被回滚提交的 SHA)。
Type(必填):必须是以下枚举值之一:
| Type | 含义 |
|---|---|
feat | 面向用户的新功能(而非面向构建脚本的新功能) |
fix | 面向用户的缺陷修复(而非对构建脚本的修复) |
docs | 仅文档变更 |
style | 格式、缺失分号等,不涉及生产代码变更 |
refactor | 生产代码重构,如变量重命名 |
chore | 更新构建任务等,不涉及生产代码变更 |
perf | 提升性能的代码变更 |
test | 补充缺失的测试或重构测试,不涉及生产代码变更 |
build | 影响 CI/CD 流水线、构建系统或外部依赖的变更(示例 scope:jenkins、makefile) |
ci | DevOps 为 CI 目的提供的变更 |
revert | 回滚某次之前的提交 |
Scope(可选):本仓库没有预定义 scope,可自定义以增强清晰度,例如示例中的fix(script):。
Subject(必填):对变更的简洁描述,要求:
- 使用祈使句、现在时:写
change,不写changed或changes; - 首字母不大写;
- 结尾不加句号(
.)。
Body(可选):与 subject 相同,使用祈使句、现在时;应包含变更动机,并与旧行为进行对比。
Footer(可选):承载两类信息:
- Breaking Changes:必须以
BREAKING CHANGE:开头(后跟一个空格或两个换行),其余部分描述破坏性变更的具体内容; - Issue 关闭引用:
Closes: #xxx,可一次引用多个 Issue。
四、Changelog 登记规范
影响 EMQX 功能行为的变更,必须在changes目录下以独立 markdown 文件描述。这一要求不仅是文档约定,仓库中还提供了 CI 脚本强制校验文件名模式。
4.1 文件命名模式
changes/ee/(feat|fix|perf|breaking)-<PR-id>.en.md各字段含义:
feat | fix | perf | breaking:变更类型——新功能(feat)、缺陷修复(fix)、性能改进(perf)或破坏性变更(breaking);PR-id:GitHub PR 编号。由于 PR 创建前无法预知编号,常见的做法是在单独的提交中补充 changelog 条目(即 PR 合入后补一条);en:ISO 639-1 语言代码,表示该 changelog 条目的语言。目前仓库只接受英文条目。
4.2 仓库内的真实产物印证
打开 changes/ee 目录可以看到大量符合该模式的实际文件,例如:
feat-14040.en.md、feat-14479.en.md(新功能)fix-xxx.en.md(修复)perf-xxx.en.md(性能改进)breaking-14765.en.md、breaking-14865.en.md(破坏性变更)
以feat-14040.en.md为例,其内容是一句紧凑的英文描述:"Added timeouts to the internal RPC calls during node rebalance. Previously, the rebalance process could hang if a node was unresponsive."(为节点再平衡期间的内部 RPC 调用添加超时;此前若节点无响应,再平衡过程可能挂起)。可见条目要求非常紧凑,同时保留"动机 + 旧行为对比"的表述结构。
4.3 CI 强制校验(源码佐证)
脚本 scripts/check-changes-filename-pattern.sh 在 CI 中执行,通过git diff --diff-filter=A找出新增文件,并对以fix-、feat-、perf-开头的文件强制匹配以下模式,否则直接报错退出:
^changes/ee/(fix|feat|perf)-[0-9]+\.en\.md$也就是说,如果你的 changelog 文件放错了目录(如changes/ce/)、类型前缀不在枚举内、PR 编号不是纯数字,或语言后缀不是.en.md,CI 都会拦截。CONTRIBUTING.md 中给出的模式是(feat|fix|perf|breaking)四种前缀,而 CI 脚本只校验fix|feat|perf三种——breaking条目同样存在于仓库中(如breaking-14765.en.md),说明breaking是约定中的合法前缀,但 CI 正则对未以这三类前缀开头的文件不强制。
4.4 Changelog 的自动化生成(源码佐证)
仓库还提供了半自动化的 changelog 生成脚本 scripts/generate-changelog.sh:
- 输入:PR 编号(命令行参数或环境变量
GITHUB_PULL_REQUEST_NUMBER); - 流程:从远端拉取 PR 的 base 与 head SHA,计算 diff,调用 OpenAI 或 Gemini API 将 diff 分类为
feat/fix/perf并生成两句以内的紧凑摘要,随后按changes/ee/${PREFIX}-${PR_NUMBER}.en.md写入文件; - 在 GitHub Actions 环境下,脚本还会自动提交该文件并推回 PR 分支。
由此可以看出,changelog 文件的类型前缀(feat/fix/perf)与 commit message 的 type 语义保持一致,整条工程链路(提交 → PR → changelog → 版本记录)是打通的。而changes/目录下的版本文件(如 changes/6.3.1.en.md、changes/e5.9.1.en.md)则汇集了这些条目,构成每个版本对外发布的变更说明。
五、实操速查:一次规范提交的完整流程
综合以上规则,向 EMQX 提交一次合规改动的推荐流程如下:
- 定位基线分支:确认改动影响的最早
dev-XX分支;不确定就选最新的dev-XX,并在 PR 描述中说明; - 创建分支并开发:基于目标 dev 分支切出特性分支;
- 提交:按
<type>(<scope>): <subject>编写 header,必要时补充 body 与 footer;整行不超过 100 字符;需要回滚时以revert:开头并注明This reverts commit <hash>.; - 登记 changelog:若改动影响 EMQX 功能,在
changes/ee/下新建<prefix>-<PR-id>.en.md(prefix ∈ feat/fix/perf/breaking),写一句紧凑的英文描述;PR 编号未知时可稍后补提交; - 发起 PR:目标分支选 dev 分支而非 release 分支,不要对多条 dev 分支重复开 PR;等 CI(含
check-changes-filename-pattern.sh)通过。
六、常见问题(FAQ)
Q1:我的修复同时影响 5.8 与 6.x,该往哪条分支提交?答:向受影响的最早的仍活跃dev-XX分支提交(例如dev-58),同步链会把它自动带到后续所有分支;切勿分别向多条 dev 分支开 PR。
Q2:不确定 scope 该怎么写?答:scope 是可选项且无预定义列表,可以不写,或使用能准确描述改动范围的短语(如fix(script):)。
Q3:changelog 文件里的 PR 编号还没确定怎么办?答:按规范在 PR 创建后的单独提交中补充,文件名里的 PR-id 以实际 PR 编号为准;CI 会校验文件必须位于changes/ee/且格式为{fix|feat|perf}-<数字>.en.md。
Q4:破坏性变更如何标记?答:在 commit 的 footer 中以BREAKING CHANGE:开头描述破坏性内容;同时按需在changes/ee/下添加breaking-<PR-id>.en.md条目。
Q5:我的提交只改了文档,需要 changelog 吗?答:changelog 针对"影响 EMQX 功能行为"的变更;仅文档类改动使用docs:类型的提交即可,通常无需 changelog 条目。
七、延伸阅读
- 分支同步链与版本推算:scripts/rel-versions
- 发布分支快进机制:scripts/ff-release-from-dev.sh
- Changelog 文件名 CI 校验:scripts/check-changes-filename-pattern.sh
- Changelog 自动化生成:scripts/generate-changelog.sh
- 变更记录存放目录:changes/ee
- 仓库主 README:README.md
【免费下载链接】emqxThe most scalable and reliable MQTT broker for AI, IoT, IIoT and connected vehicles项目地址: https://gitcode.com/gh_mirrors/em/emqx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考