news 2026/9/7 4:21:12

DeerFlow 发布工程实践:Tag 驱动发布、版本一致性门禁与 Nightly 流水线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeerFlow 发布工程实践:Tag 驱动发布、版本一致性门禁与 Nightly 流水线

DeerFlow 发布工程实践:Tag 驱动发布、版本一致性门禁与 Nightly 流水线

【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow

DeerFlow(开源长程 SuperAgent harness)采用tag 驱动的发布模型:推送v*git tag 即可触发全部发布工作流,仓库中不存在单独"提升版本号"的发布脚本。本文基于 RELEASING.md 展开,结合 scripts/bump_version.sh、scripts/verify_versions.sh 与.github/workflows/下的实际工作流源码,讲清版本源如何保持同步、CI 门禁如何拦截漏改、正式版本与 Nightly 制品如何分别产出,帮助维护者完整走通一次 DeerFlow 发布并理解每一道防线的实现原理。

1. 发布模型总览:tag 是唯一权威标识

DeerFlow 的发布流程是:维护者手动更新各处版本源 → 更新 CHANGELOG → 提交 → 打 tag → 推送 tag。推送 tag 这一动作本身触发发布工作流。仓库内没有"一键发版"脚本,辅助脚本只负责把各版本源锁定在同一个值上,而 CI 则负责验证"版本源与 tag 一致"后才允许发布。

这种设计带来两个直接约束:

  • 容器镜像的 tag 直接取自 git tag(而非仓库内任何文件),Helm chart 版本则要与 tag 校验一致;
  • 任何一个版本源落后于 tag,发布都会被门禁拦截(见第 7 节的版本门禁)。

2. 版本源(Version sources):四处必须完全一致

一次发布版本必须在以下四处出现完全相同的值,外加 git tagvX.Y.Z本身作为权威发布标识:

文件字段
backend/pyproject.tomlversion = "X.Y.Z"
frontend/package.json"version": "X.Y.Z"
deploy/helm/deer-flow/Chart.yamlversion: X.Y.Z
deploy/helm/deer-flow/Chart.yamlappVersion: "X.Y.Z"

当前仓库中四个版本源均为2.1.0:backend/pyproject.toml 的version = "2.1.0"、frontend/package.json 的"version": "2.1.0"、deploy/helm/deer-flow/Chart.yaml 的version: 2.1.0appVersion: "2.1.0",可作为对照示例。

两个衍生消费点值得单独说明:

  • 前端应用内 About 页(Settings ▸ About)不是第五个版本源,而是派生消费者:它在构建时读取frontend/package.json的 version,因此自动跟随上表,无需额外维护。
  • Nightly 构建会通过 nightly.yaml 中的APP_VERSIONbuild-arg 覆盖它,注入<base>-nightly.<YYYYMMDD>-<short_sha>形式的 chart nightly 版本串,使 nightly 镜像的 About 页能区分于正式版本。从 nightly.yaml 源码可见,该 build-arg 只对 frontend 镜像注入(matrix.component == 'frontend'时才传APP_VERSION)。

3. 辅助脚本:一次改齐四处版本源

3.1scripts/bump_version.sh <version>—— 批量提升并自校验

用法:

scripts/bump_version.sh 2.1.0

从 scripts/bump_version.sh 源码看,它的行为链是:

  1. 参数校验:容忍前导v(如v2.1.0,源码第 23 行VERSION="${1#v}"),随后用正则^[0-9]+\.[0-9]+\.[0-9]+([0-9A-Za-z.+-]+)?$校验合法 SemVer(X.Y.Z可选 prerelease/build 后缀),不合法直接退出;
  2. 逐文件存在性检查:三个目标文件任一缺失即报错退出;
  3. 用内嵌 Python 做精确替换
    • backend/pyproject.toml中顶层version = "..."(正则锚定行首,只替换第一处);
    • frontend/package.json中顶层"version": "..."(保留原缩进,最小化 diff);
    • deploy/helm/deer-flow/Chart.yamlversion:appVersion:两个字段同时替换;
    • 任何一处匹配失败都会以error: ...明确报出"未找到版本字段";
  4. 自校验:改完后自动调用scripts/verify_versions.sh <version>复核,不一致则以非零码退出;
  5. 最后打印下一步清单(更新 CHANGELOG → 提交 → 打 tag 推送)。

脚本注释明确其边界:不编辑CHANGELOG.md,也不创建/推送 git tag——这两件事保持手动,避免脚本误操作。

3.2scripts/verify_versions.sh [version]—— 一致性检查(本地 + CI 共用)

用法:

scripts/verify_versions.sh 2.1.0 # 要求所有版本源都等于 2.1.0 scripts/verify_versions.sh # 无参数:要求四个源两两相等

从 scripts/verify_versions.sh 源码看,它用awk/grep从三个文件提取出四个值并逐一比对:

Chart.yaml version: 2.1.0 Chart.yaml appVersion: 2.1.0 backend/pyproject.toml: 2.1.0 frontend/package.json: 2.1.0

关键行为:

  • 带参数模式(CI 使用):每个源都必须等于参数值,不一致时输出 GitHub Actions 注释格式::error::<文件> is '<实际值>' but expected '<期望值>'.,并以退出码 1 结束;
  • 无参数模式(本地使用):以Chart.yaml version为基准要求其余三者与之相等;
  • 失败时打印修复提示:Tip: run scripts/bump_version.sh <version> to align all sources.
  • 退出码 0 表示一致、1 表示不一致——正是发布门禁依赖的信号。

建议在打 tag 前本地先跑一遍,尽早发现漂移。

4. 标准发布流程(Release procedure)

2.1.0为例,完整四步:

第 1 步:批量提升版本

scripts/bump_version.sh 2.1.0

第 2 步:更新 CHANGELOG.md

## [Unreleased]小节重命名为## [2.1.0] — YYYY-MM-DD(注意使用 em dash),并在文件底部添加指向该 tag 发布页的链接引用(形如[2.1.0]: <GitHub Release 页面 URL for v2.1.0>),然后在上方新开一个空的## [Unreleased]小节用于下一个周期。当前 CHANGELOG.md 顶部声明遵循 Keep a Changelog 格式与语义化版本规范,[Unreleased]小节即按此积累待发布变更。

第 3 步:提交版本 + changelog 变更

git add -A git commit -m "release: v2.1.0"

第 4 步:打 tag 并推送

git tag v2.1.0 git push origin v2.1.0

推送 tag 即触发下文两个发布工作流。

5.v*tag 触发时 CI 发布什么

5.1 container.yaml —— 三个容器镜像

构建并推送backendfrontendprovisioner三个镜像到ghcr.io,tag 策略来自 container.yaml 中 docker/metadata-action 的配置:

  • type=ref,event=tag:镜像 tag 取自 git tag(如v2.1.02.1.0);
  • type=raw,value=latest,enable={{is_default_branch}}:只有默认分支才打latest
  • type=sha:额外附 commit SHA tag 便于溯源。

以 backend 为例,构建还通过UV_EXTRAS=postgresbuild-arg 将postgres依赖烘焙进发布镜像,使 K8s/Helm 多副本部署可用共享 Postgres 持久化(sqlite/redis 单副本部署不受影响)。每个构建 job 都needs: verify-versions,门禁不通过则镜像一个都不会构建。

5.2 chart.yaml —— Helm chart 的 OCI 制品

将 chart 打包并推送为 OCI artifact 到ghcr.io。用户安装方式为:

helm install deer-flow oci://ghcr.io/<owner>/charts/deer-flow --version 2.1.0

从 chart.yaml 源码看,发布前有三道检查:

Job / Step作用
validate-chart(PR 与 tag 均触发)helm lint+helm template渲染验证 + sandbox Service-type 门禁检查(scripts/check_chart_sandbox_service.sh)+ skill 上传 Ingress 策略检查(scripts/check_chart_skill_upload_size.sh)+config_version漂移检查(scripts/check_config_version.sh,确保 chart 内嵌 config_version 不落后于 config.example.yaml)
verify-versions(仅v*tag)调用可复用工作流 verify-versions.yml 做版本门禁
publish-chartneeds: [verify-versions, validate-chart],两者都通过才执行helm package+helm pushoci://ghcr.io/<owner>/charts/deer-flow

chart 文件头部注释特别说明了为何校验必须在 PR/tag 阶段做:发布到 OCI 的 chart 版本是不可变制品(GHCR 不允许覆盖同一--version),渲染坏了或 config_version 过期只能在用户安装时爆炸——所以回归必须在validate-chart拦住,而不是留给发布。

6. Nightly 构建:未发布 main 的每日快照

nightly.yaml 按计划触发(cron: "0 16 * * *", 每天 16:00 UTC)加上workflow_dispatch手动触发,从未发布的main分支发布同样的三个镜像 + chart。它有三个与正式发布的本质差异:

  1. 不经过版本门禁——没有v*tag 可比对;
  2. 不碰latesttag——latest永远钉在最近的正式v*版本上;
  3. 仅限上游仓库运行:每个 job 都 gate 在github.repository == 'bytedance/deer-flow'上,fork 上的定时触发或手动 dispatch 会跳过全部 job。此外并发组nightly设了cancel-in-progress: false,避免中途取消留下"推了一半"的制品集。

Nightly 制品(位于运行仓库 owner 名下,<date>YYYYMMDD):

  • 镜像ghcr.io/<owner>/deer-flow-{backend,frontend,provisioner}:nightly(滚动 tag,每次运行覆盖)与:nightly-<date>(按天钉住,但当天内可变——同日 re-dispatch 会覆盖它)。需要真正不可变的钉住,请使用:sha-<short>
  • Chartoci://ghcr.io/<owner>/charts/deer-flow,版本为<base>-nightly.<date>-<sha>(例如2.1.0-nightly.20260710-77a3652)。短 SHA 让每次 dispatch 的 chart 版本唯一,同日 re-dispatch 可以干净地重新发布(OCI chart 版本不可变,否则无法覆盖)。打包后的 chart 默认image.registry=ghcr.io/<owner>image.tag=nightly,因此安装时无需任何 values 覆盖即可拉取配套的 nightly 镜像:
helm install deer-flow oci://ghcr.io/<owner>/charts/deer-flow \ --version 2.1.0-nightly.20260710-77a3652

从 nightly.yaml 的preparejob 可见版本串的生成逻辑:BASE取自Chart.yamlversion:,拼上nightly.<date>与 7 位GITHUB_SHA;若BASE为空会显式报错失败(注释解释了pipefail的必要性——否则set -e抓不到 grep 漏配)。该 nightly 版本串是单一事实源:既注入 frontend 镜像的APP_VERSION(About 页展示),也写进 chart 版本号,保证"用户看到的版本"与"chart 版本"不会漂移。

另一个重要细节:chart 版本只在工作流内做 patch——仓库里的Chart.yamlvalues.yaml从不被修改。

7. 版本门禁(Version gate):漏改一个字段就整体阻断

两个发布工作流(container.yaml 与 chart.yaml)都把 verify-versions.yml 作为第一个 job调用。该可复用工作流的实现很直接:

# .github/workflows/verify-versions.yml(第 24-27 行核心逻辑) - name: Verify all version sources match tag run: | TAG_VERSION="${GITHUB_REF_NAME#v}" bash scripts/verify_versions.sh "$TAG_VERSION"

即:剥掉 tag 的前导v,调用与本地完全相同的 scripts/verify_versions.sh 脚本比对四个版本源。任何一个源与 tag 不一致,verify job 失败,所有发布 job 被跳过——没有镜像、没有 chart。门禁失败时,job 注释会指名问题文件并给出修复建议:

::error::frontend/package.json is '2.0.0' but expected '2.1.0'. Tip: run scripts/bump_version.sh 2.1.0 to align all sources.

这条链路值得注意的设计是"逻辑单点":本地脚本与 CI 门禁是同一份verify_versions.sh,本地预检与发布时校验的行为完全一致,不存在两套规则各说各话的问题。

8. Pre-releases(RC)

v2.1.0-rc1这类预发布 tag 是合法的v*tag,会触发同一套工作流。但版本源必须等于完整的预发布字符串2.1.0-rc1)——门禁做的是精确字符串比较。流程与正式版相同,只是版本号换成 rc:

scripts/bump_version.sh 2.1.0-rc1 # 更新 CHANGELOG,提交,打 tag v2.1.0-rc1,推送

bump_version.sh的 SemVer 正则允许-prerelease后缀,因此2.1.0-rc1这类值可顺利通过参数校验。

9. 门禁失败后的恢复

如果门禁因漏改某个版本源而失败:

  1. 运行scripts/bump_version.sh <version>对齐所有版本源;
  2. amend 原提交或追加一个后续提交;
  3. 删除并重建 tag,然后推送:
git tag -d v2.1.0 git tag v2.1.0 git push origin :refs/tags/v2.1.0 git push origin v2.1.0

重新推送 tag 会重新触发工作流。之所以重打 tag 是安全的,是因为门禁失败时所有制品都不会发布——坏 tag 下什么都没推送到 GHCR,不存在需要覆盖的镜像或 chart。

10. lark-cli 沙箱镜像:独立于v*发布的生命周期

两个可选的 Lark 沙箱运行时镜像——lark-cli-init(Pattern A)与lark-cli-broker(Pattern B)——不属于v*发布的一部分。它们跟随上游larksuite/cli的版本,因此通过 lark-cli-images.yaml 独立发布:

  • 触发方式:workflow_dispatch(带lark_cli_version输入,例如v1.0.65),或推送lark-cli-v*tag(版本号取自前缀之后,如lark-cli-v1.0.65v1.0.65);
  • 构建多架构镜像(platforms: linux/amd64,linux/arm64),推送ghcr.io/<owner>/deer-flow-{lark-cli-init,lark-cli-broker}:<lark-cli-version>
  • 同样 gate 在github.repository == 'bytedance/deer-flow';不参与verify-versions门禁(它的版本号是 lark-cli 的发布版本,不是 DeerFlow 的),也从不触碰latest

这两个功能保持 opt-in:provisioner 在LARK_CLI_INIT_IMAGE/LARK_CLI_BROKER_IMAGE指向已发布的 tag 之前会忽略它们。

11. Post-release

可选地从 tag 起草一条GitHub Release,把对应的CHANGELOG.md小节作为 release notes 粘贴进去——changelog 底部的链接引用正是指向这些 release URL。

针对 2.1.0 这个 chart 首次发布的一次性说明:charts/方案之前基于裸包ghcr.io/<owner>/deer-flow的 nightly 构建将停留在旧包上。2.1.0 之后该包不会再有新版本;当确认没有用户仍在拉取它时,应删除它或撤销其可见性。

12. 小结:一次 DeerFlow 发布的防线地图

环节防线依据
本地改版本bump_version.sh改后自跑 verifyscripts/bump_version.sh
本地打 tag 前verify_versions.sh互检/对齐检查scripts/verify_versions.sh
PR 阶段validate-chart:lint、渲染、config_version 漂移.github/workflows/chart.yaml
v*tag 阶段verify-versions可复用工作流,失败则镜像与 chart 全部跳过.github/workflows/verify-versions.yml
恢复对齐版本源 → 重建 tag → 重推,坏 tag 下无制品需清理RELEASING.md
Nightly无门禁但限上游仓库,latest永不动.github/workflows/nightly.yaml

整个体系的核心思想可以概括为:git tag 是唯一权威版本标识,其余所有版本源都是它的从属副本;本地脚本负责让副本跟上,CI 门禁负责在副本落后时拒绝发布一切制品。理解了这条主线,DeerFlow 的发布流程中每个脚本、每个 job、每道 gate 的取舍就都自洽了。

【免费下载链接】deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tasks that could take minutes to hours.项目地址: https://gitcode.com/GitHub_Trending/de/deer-flow

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

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

Minecraft 1.21.11离线服务器搭建教程:域名联机全攻略

自己开一个 Minecraft 服务器邀请朋友联机&#xff0c;最常见的一个需求就是“离线可进”。这意味着朋友不一定都购买了正版 Minecraft&#xff0c;或者客户端启动器没有登录正版账号&#xff0c;而服务器也不用向 Mojang 的鉴权服务器验证玩家身份。本文以题目给出的 1.21.11 …

作者头像 李华
网站建设 2026/9/7 4:18:51

YOLOv8结构拆解与改进实战:从数据诊断到消融实验

做毕业设计选 YOLOv8&#xff0c;是目前很多同学的目标检测标配。但一个常见的现象是&#xff1a;代码下载很顺利&#xff0c;训练完一看 mAP&#xff0c;效果并不理想。于是很多人开始在网上搜索各种改进模块&#xff0c;注意力机制、小目标检测头、BiFPN、新损失函数……一样…

作者头像 李华
网站建设 2026/9/7 4:16:38

精读Mask2Former:掩码注意力如何统一语义、实例与全景分割

分割这个方向&#xff0c;论文多到什么程度呢&#xff1f;光是用关键词去搜&#xff0c;语义分割、实例分割、全景分割、点云分割、遥感分割、医疗分割&#xff0c;每一类都能拉出几十上百篇&#xff0c;更不用说这两年Transformer和Mask类方法爆发之后&#xff0c;几乎每周都有…

作者头像 李华