首页
/ LobeHub Minor Release 工作流实战指南:从 canary 分支到 v2.2.0 的自动化发布全流程

LobeHub Minor Release 工作流实战指南:从 canary 分支到 v2.2.0 的自动化发布全流程

2026-09-06 18:36:36作者:吴年前Myrtle

本指南以 LobeHub 仓库中的 Minor Release(次版本迭代发布)工作流为核心,完整讲解一条「从 canary 分支拉出 release 分支 → 创建带精确版本号的 PR → CI 依据 PR 标题自动完成版本号提升、打标签、生成 GitHub Release 并回同步代码」的工程化发布流水线。读完你将掌握 Minor Release 的每一步命令行操作、PR 标题的严格格式约束、与 Patch/Weekly/Hotfix 发布的关系,以及它背后的 auto-tag-release.yml 触发原理与 release-notes 写作规范,可直接应用于你所在团队的版本发布实践。

一、Minor Release 在 LobeHub 版本体系中的定位

LobeHub 的主开发分支是 canary,日常开发全部汇聚于此。发布时以 canary 为源拉出发布分支,合并进 main,随后由名为 auto-tag-release 的 CI 工作流自动完成后续全部动作。相关的完整说明沉淀在 版本发布 Skill 入口 及其 references 目录 中。

SKILL.md 的概览表可以看到,实际使用中主要只有两种发布类型(major 极少出现,可忽略):

类型 适用场景 频率 源分支 PR 标题格式 版本号
Minor 特性迭代发布 约每 4 周 canary 🚀 release: v{x.y.0} 人工设定
Patch 周更 / 热修复 / 模型 / DB 迁移 约每周或按需 canary 或 main 自定义(如 🚀 release: 20260222 自动 patch +1

Minor Release 是"特性迭代发布"的载体:当一批新功能在 canary 上积累到可发布的里程碑时(例如 v2.1.xv2.2.0),便按本流程发布。它的关键特征是版本号由人精确指定——这与 Patch 由 CI 自动 patch +1 的机制形成鲜明对比。

二、发布前的分支预检(所有发布类型的通用前置)

在创建发布分支前,必须核验源分支是否正确,这是 SKILL.md 中规定的强制预检:

  • Weekly Releaserelease/weekly-*):必须从 canary 拉分支;
  • 其他所有 release / hotfix 分支:必须从 main 拉分支,并验证祖先关系:
git merge-base --is-ancestor main <branch> && echo OK

若分支基于错误的源,必须从正确基线重建。这一步能避免把尚未评审的代码意外带入发布线。

三、Minor Release 五步操作流程

minor-release.md 给出了完整的执行步骤,下面逐条展开并补充底层细节。

第 1 步:从 canary 拉出 release 分支

git checkout canary
git pull origin canary
git checkout -b release/v{version}
git push -u origin release/v{version}

分支命名规范为 release/v{version},例如 release/v2.2.0。务必先 git pull 同步最新的 canary 状态,保证发布分支包含当前全部待发布特性。

第 2 步:确定版本号

读取 package.json 中的当前版本,计算下一个 minor 版本:例如当前为 2.1.x,则下一次 minor 为 2.2.0(patch 位置归零)。

这一"由人算版本号"的过程在仓库脚本中也有对应的自动化实现:scripts/releaseWorkflow/index.ts 使用 semver.inc(currentVersion, type) 计算新版本,并在交互式确认面板(consola.box)中展示 Current / New / Type / Branch / Target 信息后再执行创建分支与提 PR 的动作。也就是说,仓库里提供了一条便捷的命令行脚本帮你完成"算号 + 拉分支 + 提 PR"的组合操作。

第 3 步:向 main 创建 PR

gh pr create \
  --title "🚀 release: v{version}" \
  --base main \
  --head release/v{version} \
  --body-file release_body.md

[!IMPORTANT] PR 标题必须严格匹配 🚀 release: v{x.y.z} 格式。CI 通过对标题做正则匹配来确定精确的版本号,任何格式偏差都会"落入 patch 检测"分支,导致发布类型被误判。

第 4 步:撰写 PR 正文(即发布说明)

PR body 需要按照 release-notes-style.md 的规范撰写,因为该 body 在合并后会被 CI 直接用作 GitHub Release 内容。比较基线(compare base)取 main 上最新的 semver tag

git fetch origin main canary --tags
PREV_TAG=$(git describe --tags --abbrev=0 origin/main --match 'v*.*.*' --exclude '*-canary*' --exclude '*-nightly*')
echo "$PREV_TAG"

并验证该 tag 可从发布分支到达:

git merge-base --is-ancestor "$PREV_TAG" origin/release/weekly-{YYYYMMDD} && echo OK

为什么不能用"上一个 Weekly 的 tag"? 在两次 Weekly 之间,hotfix(如 v2.1.54v2.1.55)会直接合入 main,再经由 sync-main-to-canary 回灌到 canary。因此 main 上最新的 semver tag 才是正确的"上一版本",误用上一次 Weekly 的 tag 会造成提交区间漏算、Since v… 版本号陈旧。

第 5 步:合并后的自动触发

PR 合并后无需人工干预,auto-tag-release 会自动完成:版本号提升、打 annotated tag、创建 GitHub Release、触发 sync-main-to-canary 回灌分支。这部分机制详见下文第五节。

四、配套脚本:release:branch

除了手写 git/gh 命令,仓库还封装了交互式脚本,入口注册于 package.json

bun run release:branch          # 交互式选择版本类型
bun run release:branch --minor  # 直接指定 minor

对应实现位于 scripts/releaseWorkflow/index.ts,脚本的执行链路为:

  1. checkGitRepo() 校验当前目录是 Git 仓库;
  2. 拉取并切换到源分支(默认实现对应开发主干);
  3. getVersionTypeFromArgs() 解析 --patch / --minor / --major 参数,无参数时进入 selectVersionTypeInteractive() 交互选择;
  4. 基于 package.json 当前版本用 semver 计算新版本号,交互面板展示发布信息并二次确认;
  5. createReleaseBranch() 创建 release/v{version} 分支并推送;
  6. createPullRequest() 调用 gh pr create,标题固定为 🚀 release: v${version},目标分支 --base main,并附带 --label "release"

注意:该脚本内部仍保留早期以 dev 分支为开发主干的命名习惯,而当前 版本发布 Skill 规定的主开发分支是 canary,实际执行发布时应以 Skill 文档的 canary 说明为准。

五、合并后 CI 到底做了什么:auto-tag-release 触发规则

Minor Release 的"版本号取自 PR 标题"这一设计,在 .github/workflows/auto-tag-release.yml 中有精确的代码级证据。该工作流监听对 main 分支的 PR 关闭事件(pull_request_target + closed),且仅在 github.event.pull_request.merged == true 时执行。

检测优先级

CI 依据以下优先级判定是否发布(见 auto-tag-release.yml):

1. Minor Release(标题精确取号)

对 PR 标题执行严格正则匹配:

if [[ "$PR_TITLE" =~ ^🚀[[:space:]]+release:[[:space:]]*v([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?)$ ]]; then

命中 🚀 release: v{x.y.z}(支持 -prerelease+build 后缀的可选扩展),则从标题提取版本号写入 version 输出。这正是 minor-release.md 强调"标题格式严格"的原因——正则一旦失配,should_tag 即为 false。

2. Patch Release(自动 patch +1),按以下优先级:

  • 分支名命中:head 分支为 hotfix/*release/* 时直接触发,绕过标题检测;
  • 标题前缀兜底:标题以 style/feat/fix/refactor/hotfix/build(含对应 gitmoji 表情前缀)开头时触发。

3. 不触发docschorecitest 等前缀合入 main 不会触发发布。

Patch 版本的解析方式

Patch 场景下版本号不用写进标题,CI 从 package.json 读取当前版本,先 npx semver -c 收敛到稳定基线(如 2.0.0-beta.12.0.0),再执行 -i patch 得到下一 patch(→ 2.0.1)。对比可见:Minor 由标题注入版本,Patch 由仓库现状推导版本,两条路径在 Set context 步骤汇聚为统一的 VERSIONKIND 环境变量。

发布后的自动动作

should_tag=true 且目标 tag 尚不存在时(git rev-parse v$VERSION 探测防重),CI 依次执行(auto-tag-release.yml):

  1. 提升 package.json 版本:写入新版本并提交 🔖 chore(release): release version v{x.y.z} [skip ci]
  2. 生成 changelog:调用 bun run workflow:changelog:genbun run workflow:changelog(对应 scripts 下的 changelogWorkflow),更新根目录 CHANGELOG.mdchangelog 目录;
  3. 创建 annotated taggit tag -a "v$VERSION",tag 消息中携带发布 PR 号与作者,然后推送到远端——注意 tag 打在版本提升后的提交 SHA上,而非 PR 的 merge commit;
  4. 创建 GitHub Release:通过 softprops/action-gh-release 以 PR 描述作为 Release body 发布;
  5. 回灌分支:调用 gh workflow run sync-main-to-canary.yaml,将 main 同步回 canary,保持两个分支的代码一致。

这一连串动作印证了 Minor Release 的硬性规则:不要手工改 package.json、不要手工打 tag——CI 会在合并后全自动完成。

六、Hard Rules:Minor Release 专属的硬性红线

minor-release.md 中列出的 Minor 专属规则必须严格执行:

  • PR 标题格式是严格的🚀 release: v{x.y.z},任何偏差都会落入 patch 检测路径(详见上文 CI 正则);
  • 禁止手工修改 package.json 版本号——版本提升由 CI 在合并后统一完成;
  • 禁止手工创建 tag——CI 负责打标签与发布;
  • Highlights 条目数通常为 8–12 条——遵循 release-notes-style.md 的规模启发式判断。

这些红线共同保证了"PR 标题是唯一的事实来源(single source of truth)",杜绝人工操作与 CI 状态之间的不一致。

七、如何撰写 Minor 的 Release Notes(长格式规范)

Minor / Weekly 均使用 release-notes-style.md 定义的 长格式(Long-Form) 结构。它遵循四条定位原则:数据置顶、先叙事再结构、深度但易扫读、贡献者前置。

7.1 编写前必须采集的输入

  1. 比较区间 <prev_tag>...<current_tag>
  2. 发布指标(commits、merged PRs、resolved issues、contributors,可选 files 变更量);
  3. 按领域归类的高影响变更(core loop、platform/gateway、UX、tooling、security、reliability);
  4. 贡献者列表(含突出的个人贡献);
  5. 已知风险 / 迁移 / 发布说明(如有)。

若指标无法可靠计算,宁可省略也不要猜测。

7.2 计算输入的硬性规则:一切来自 git,禁止凭空捏造

该文档把"臆造 PR 号、错选 Since v… 基线"列为该 Skill 的头号失败模式,并给出三条铁律:

① 比较基线 = main 上最新 semver tag(命令见本文第三节第 4 步),不要目测 tag 列表。

② PR 号必须来自 commit subject

git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" \
  --pretty=format:'%s' --no-merges \
  | grep -oE '\(#[0-9]+\)$' \
  | sort -u > /tmp/release_prs.txt

正文中出现的每个 (#XXXX) 都必须存在于该文件中,禁止凭记忆推断 PR 号。

③ 指标来自 git 计数

PR_COUNT=$(wc -l < /tmp/release_prs.txt | tr -d ' ')

COMMIT_COUNT=$(git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" --no-merges --pretty=format:'%h' | wc -l | tr -d ' ')

CONTRIBUTOR_COUNT=$(git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" --no-merges --pretty=format:'%an' \
  | sort -u \
  | grep -viE '^(lobehubbot|LobeHub Bot|renovate\[bot\])$' \
  | wc -l | tr -d ' ')

作者名转 GitHub 身份需通过 gh pr view "$PR" --json author --jq '.author.login' 解析,不能直接拿 git %an 当 handle 使用。

④ 发布前强制校验:对正文与真实提交区间做差集比对:

grep -oE '#[0-9]+' release_body.md | sort -u > /tmp/body_prs.txt
sed 's/[()]//g' /tmp/release_prs.txt > /tmp/release_prs_clean.txt

# 输出必须为空
comm -23 /tmp/body_prs.txt /tmp/release_prs_clean.txt

只要差集非空,说明正文引用了该区间内未合并的 PR,必须停下修正后才能发布。

7.3 长格式的规范结构

Minor 与 Weekly 必须按下述顺序组织(release-notes-style.md),Hotfix 与 DB Migration 则走短格式变体:

  1. # 🚀 LobeHub Release (<YYYYMMDD>)
  2. 元数据行:Release Date + Since <Previous Version> 指标
  3. 一句引用形式的发布主旨(1–2 行)
  4. ## ✨ Highlights(大型发布 8–12 条;Weekly 3–8 条)
  5. 领域分块(可用 ### 子节):🏗️ Core Agent & Architecture📱 Platforms / Integrations🖥️ CLI & User Experience🔧 Tooling🔒 Security & Reliability📚 Documentation(可选)
  6. ## 👥 Contributors
  7. **Full Changelog**: <prev>...<current>

长短发布之间用 --- 分隔。模板骨架同样保存在 release-notes-style.md,可直接复制使用。

对应的完整示例可见 weekly-release.md,它演示了元数据行、Highlights、领域分块、贡献者与 Full Changelog 的组合写法。

7.4 贡献者排序与团队名单

贡献者渲染为单一扁平列表(不再拆分社区/团队子节),排序规则为:社区贡献者在前、团队成员在后,组内按 PR 数降序;机器人(@lobehubbotrenovate[bot])单独放一行 maintenance 说明。团队名单以 release-notes-style.md 中的 LobeHub team roster 为准;名单之外出现的贡献者默认按社区贡献者处理,并应询问是否将其加入名单。

7.5 写作红线与风格

  • 不虚构指标:所有数字必须可溯源;
  • 不使用空泛头条:每条 bullet 必须同时包含能力 + 影响;
  • 不从内部视角表述:以用户/运维视角行文;
  • 安全相关修复必须显式声明
  • 术语全篇一致,不得在不同章节换名;
  • 迁移或破坏性变更不得埋没:提升到独立章节或用 callout 强调;
  • 加粗仅用于能力名,不用于整句;
  • 标题层级不超过三级。

八、Minor 与其他发布形态的分工与互补

理解 Minor Release,还需了解它和相邻发布形态的关系(详见 patch-release-scenarios.mdSKILL.md 的触发规则):

形态 场景 关键动作
Weekly(周更) canary → main,聚合一周变更 release/weekly-{YYYYMMDD} 分支;标题 🚀 release: {YYYYMMDD},无版本号
Bug Hotfix 从 main 直接出发的紧急修复 hotfix/* 分支;gitmoji 标题(如 🐛 fix: …);合并即自动 patch +1
New Model Launch 新模型/供应商支持 普通 feat/style PR,标题前缀命中即自动触发
DB Schema Migration 需独立发布的库表变更 release/db-migration-* 分支 + cherry-pick;短格式 changelog,强调迁移影响与回滚
Minor(本文) 特性迭代里程碑 release/v{x.y.0} 分支;标题精确携带版本号

两条关键边界值得注意:其一,Weekly、Minor 从 canary 出分支,而 Hotfix、DB Migration 从 main 出分支;其二,Hotfix 与 DB Migration 使用的是另一套短格式发布说明结构——无 Highlights、无领域分块、无 Contributors 长名单,而是以 Hotfix Scope / What's Fixed / Upgrade / Owner 为主(模板见 hotfix.mddb-migration.md)。判断该用哪种结构,可依据规模启发式:Minor/里程碑走长格式(Highlights 8–12 条),Weekly 走精简长格式(4–8 条),Hotfix 与 DB Migration 走短格式。

九、写在最后:从文档到流水线的全局视角

回到 minor-release.md 开篇的那句话——Minor Release 的目的是发布一个新的次版本(如 v2.2.0),大约每 4 周一次,PR 标题携带精确版本号,由 CI 解析标题驱动后续全部动作。这条工作流之所以能在 LobeHub 稳定运转,依赖四层设计互为咬合:

  1. 人只决策"何时发、发什么版本",其余交给自动化;
  2. PR 标题是唯一的机器可读事实源,正则解析保证确定性;
  3. 版本提升、打 tag、发 Release、回灌分支全部由 auto-tag-release.yml 接管,人工介入点被压缩到最少;
  4. 发布说明的数据全部强制来自 git(tag、commit、PR 号),配合发布前差集校验,从机制上杜绝了"发布笔记与真实变更不一致"这一最常见的工程事故。

当你所在团队需要把"发布"从手工操作升级为流程化、可审计的流水线时,LobeHub 这套以"标题为准、CI 执行、git 数据为证"的 Minor Release 实践,是一份可以直接借鉴的完整范本。

登录后查看全文
热门项目推荐
相关项目推荐