Flower Framework 发布实战:两步人工流程驱动全自动化 minor 版本发布
2026/9/17 18:58:17 网站建设 项目流程

Flower Framework 发布实战:两步人工流程驱动全自动化 minor 版本发布

【免费下载链接】flowerFlower: A Friendly Federated AI Framework项目地址: https://gitcode.com/GitHub_Trending/flo/flower

本文基于 Flower 仓库的发布文档 contributor-how-to-release-flower.rst,完整讲解 Framework 子包(flwr)minor 版本的发布机制:如何用一条gh workflow run命令生成发布 PR,如何审查生成的 changelog 并合并,以及合并之后由 GitHub Actions 自动完成的打 tag、发布 PyPI 包、提升 Docker 镜像标签、构建文档和创建 GitHub Release 等全部后续动作。读完后你将掌握 Flower 的完整发布操作路径,并能理解每条自动化检查背后的源码依据。

发布流程总览:人工只做两件事

Flower 的 Framework minor 发布绝大多数工作是自动化的。人工发布流程只有两步:

  1. 触发发布准备 workflow(framework-release-prepare.yml),由它创建一个发布 PR;
  2. 审查生成的 changelog,确认无误后批准并合并该 PR。

发布 PR 合并之后,剩余所有发布步骤(打 tag、发 Python 包、发 Docker 镜像、构建文档、创建 GitHub Release)都由 GitHub Actions 自动执行。文档中特别强调:不要手动创建 release tag,不要手动发布 Python 包、Docker 镜像或 GitHub Release——这些全部由 finalize workflow 接管。

整个流程涉及四个 workflow 文件和两个 Python 脚本,对应关系如下:

阶段文件作用
人工触发framework-release-prepare.yml生成 changelog、更新版本簿记、创建/刷新发布 PR
PR 自动检查framework-release-check.yml校验版本状态、changelog 与预构建产物
合并后自动执行framework-release-finalize.yml打 tag、发布 wheel/sdist/Docker 镜像、建 GitHub Release
日常预构建framework-commit-artifacts.ymlmain上每个 commit 预构建并上传产物
changelog 生成update_changelog.py从 PR 标题与贡献者记录生成发布 changelog
版本簿记update_version.py把仓库中的版本号推进到下一个开发周期

第一步:触发发布准备 workflow

触发命令与版本格式约束

使用 GitHub CLI 触发发布准备 workflow:

gh workflow run framework-release-prepare.yml \ --repo flwrlabs/flower \ -f version=X.Y.0

也可以在 GitHub Actions 的 Web 界面中手动触发 "Framework Prepare Minor Release" workflow(即 framework-release-prepare.yml 文件对应的 workflow)。

版本必须使用X.Y.0格式,例如1.34.0。这不是文档上的口头约定,而是 workflow 中硬编码的正则校验——在 framework-release-prepare.yml 中,Validate version and pin main步骤用^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.0$校验输入,不满足即报错Invalid Framework minor release version并退出。这意味着 patch 版本号(如1.34.1)不能通过该流程发布,minor 发布只接受X.Y.0

workflow 实际做了什么

触发之后,workflow 按以下顺序工作(均可在 framework-release-prepare.yml 中逐行核对):

  1. 固定(pin)发布来源 commit。workflow 执行git fetch origin main并把origin/main的 HEAD 记录为release-source-sha(第 48 行起),同时从版本推导出维护分支系列名(去掉末尾的.0)。

  2. 前置条件检查(第 62 行起):

    • 远程仓库中refs/tags/framework-X.Y.0标签和refs/heads/release/framework-X.Y维护分支都必须不存在,防止重复发布同名版本;
    • 通过git show <sha>:framework/pyproject.toml读取被 pin 的 commit 中声明的 Framework 版本,必须与请求的版本一致——这保证发布的是"版本簿记已经处于该版本"的那个 main 快照。
  3. 选择自动化分支(第 117 行起):分支名固定为automation/release/framework-X.Y.0。若远程已有该分支(说明之前已触发过同一版本),则拉取已有分支进入refresh模式;否则从 pin 的 commit 新建分支(create模式)。

  4. 写入发布元数据:把versionrelease_source_sha写入 .github/framework-release.json。该文件是贯穿整个流程的"机器管理"状态文件——当前仓库中它记录的就是最近一次发布1.37.0及其来源 SHA,后续的 check 和 finalize workflow 都从它读取这两个值。

  5. 生成 changelog:运行 framework/dev/update_changelog.py:

    python framework/dev/update_changelog.py \ --version "${VERSION}" \ --source-sha "${RELEASE_SOURCE_SHA}"

    随后还会调用一个 LLM 步骤(workflow 中的openai/codex-action,第 169 行起)对草稿 changelog 做润色。

  6. 更新版本簿记:运行python framework/dev/update_version.py --released-version "${VERSION}",把仓库中所有版本引用推进到下一个开发周期(详见下文"版本簿记更新")。

  7. 提交并推送(第 185 行起):以机器人身份提交所有变更(commit message 为feat(framework): Prepare X.Y.0 release),并推送到自动化分支。这里有两个防御性细节:首次运行(create模式)如果没有任何变更,workflow 会直接失败,避免创建空 PR;刷新模式(refresh)下如果变更已全部存在,则跳过提交与推送,保证重复触发是幂等的。

  8. 创建或刷新发布 PR(第 223 行起):首次运行用gh pr create创建指向maindraft PR,标题为feat(framework): Prepare X.Y.0 release,PR 描述中自动附上版本号、来源 SHA 和 commit 链接;刷新模式则用gh pr edit更新 PR 描述。

发布前有新 commit 落到 main 怎么办

文档明确说明了这个高频场景:如果在发布 PR 合并之前main上又有新变更,用相同的版本号再次触发 workflow 即可。此时不会新建 PR,而是刷新(refresh)已有的发布 PR,并把发布来源重新 pin 到当前main的 HEAD。从源码结构看,这正是"refresh 模式"的设计目的:workflow 检测到远程已存在automation/release/framework-X.Y.0分支时,会在新的 pin 点上重跑 changelog 生成、版本簿记和提交推送,使 PR 内容始终对应最新的 main 快照。

changelog 是如何生成的

framework/dev/update_changelog.py 是整个发布中"文档自动生成"的核心,其工作流程可以逐函数核对:

  • 确定发布区间_get_previous_release_taggit describe --first-parent --tags --match=framework-*找到 pin 点之前最近的一个framework-*发布 tag,再以上一个tag..来源SHA作为本次发布的 commit 区间(_get_commits)。
  • 提取 PR 编号_get_pr_numbers从 squash commit 摘要末尾的(#12345)模式中提取本次区间内的唯一 PR 编号。
  • 拉取 PR 元数据:对每个 PR 并行(最多 8 线程)执行gh pr view --jsongh pr diff,缓存在.cache/update_changelog/下,重复触发时直接复用缓存。
  • 按标题分组:PR 标题按 dev/changelog_config.toml 中定义的pattern_template正则解析出type(project:scope): subject结构。type取值包括cidocsfeatfixrefactorbreakproject取值包括frameworkagentbaselinesdatasetsexamples等。标题到 changelog 章节的映射见PR_TYPE_TO_SECTIONfeat归入 "New features",docs归入 "Documentation improvements",break归入 "Incompatible changes",ci/fix/refactor归入 "Other changes";若 PR 带有主题标签(topic label),则优先进入同名自定义章节。
  • 过滤规则datasetshubintelligence三个子项目的 PR 不进入 Framework changelog(SKIPPED_CHANGELOG_PROJECTS);bot 账号(如github-actions[bot]copilot)不会出现在贡献者名单中。
  • 产出两个文件_update_release_file生成framework/docs/source/changelog/vX.Y.0.md,首行是## vX.Y.0 (YYYY-MM-DD),含贡献者致谢(git shortlog顺序)和按时间排序的 PR 条目;_update_index```{include} vX.Y.0.md插入 changelog 索引 的顶部。重复运行是幂等的:已存在的 PR 编号会被跳过,只补充新增 PR。

版本簿记更新:为下一个开发周期铺路

framework/dev/update_version.py 负责"发布版本 X.Y.0 后,把开发线推进到 X.(Y+1).0"。--released-version X.Y.0会派生出下一个 minor 版本,并更新(_collect_updates):

  • framework/pyproject.toml 的version与 framework/uv.lock 中 editableflwr包版本 → 推进到下一个 minor;
  • framework/docs/source/conf.py 的release.. |stable_flwr_version|替换值 → 下一个 minor;
  • baselines/docs/source/conf.py、examples/docs/source/conf.py 的release→ 指向刚发布的 X.Y.0;
  • 三个 Docker Compose 文件(framework/docker/complete/compose.yml及 distributed 下的 client/server compose)中的FLWR_VERSION默认值 → 下一个 minor;
  • examples/下所有 FAB v1 示例的flwr-version-target更新为发布版本,并递增其自身 patch 版本;
  • 四个 Docker README(base/superexec/superlink/supernode)中的稳定标签组与latest指向。

该脚本所有替换都是"必须命中且唯一"的强校验:期望的模式没找到就抛ValueError,避免版本簿记被静默遗漏。它还有一个--check只读模式,这正是发布 PR 自动检查所用的形态(见下节)。

发布 PR 的自动检查

发布 PR 一旦创建(或每次刷新),framework-release-check.yml 会自动运行。它只对源分支名以automation/release/framework-开头、目标为main的 PR 生效,从 .github/framework-release.json 读取版本与 pin 的 SHA 后做三项校验:

  1. 确定性版本状态检查(第 52 行起):以只读模式运行python framework/dev/update_version.py --released-version X.Y.0 --check。如果工作区与"发布准备脚本应当产生的版本状态"不一致(即--check检测到还有文件需要变更),检查失败——这保证 PR 里的版本簿记确实由工具生成且完整。
  2. changelog 检查(第 60 行起):要求三件事同时成立——
    • framework/docs/source/changelog/vX.Y.0.md文件存在;
    • changelog 索引 中包含```{include} vX.Y.0.md指令;
    • changelog 首行标题必须是## vX.Y.0 (今天 UTC 日期),防止 PR 陈放太久后日期失真。
  3. 发布产物就绪检查(第 86 行起):从https://artifact.flower.ai/framework/commits/<pin的SHA>/digests.json下载该 commit 的 Docker 产物索引,并用jq严格校验其 schema:schema_version == 1commit_sha与 pin 的 SHA 一致、每个镜像条目都有仓库名、标签和非空的sha256索引摘要。这保证了"被 pin 的那个 commit 的预构建产物确实存在"。

三项检查全部通过后,发布 PR 就具备了合并条件。

第二步:审查并合并发布 PR

人工审查的核心对象是生成的 changelog 文件:framework/docs/source/changelog/vX.Y.0.md。在正式发布前可以对其进行任何必要的人工编辑(例如修正条目措辞、调整分组)。当 PR 准备就绪时:

  1. 如果 PR 仍是 draft 状态,将其标记为 ready for review;
  2. 批准(approve)该 PR;
  3. 合并(merge)到main

到此人工发布流程结束。再次强调文档的红线:不要手动创建 release tag、不要手动发布 Python 包、Docker 镜像或 GitHub Release。

合并之后:finalize workflow 自动完成什么

合并发布 PR(pull_request_targetclosed事件,且源分支以automation/release/framework-开头、目标为main)会自动触发 framework-release-finalize.yml。workflow 从合并提交中的 .github/framework-release.json 读取"最新一次准备运行所记录的发布来源 commit",然后依次执行:

  1. 创建 tag 与维护分支(第 59 行起):创建framework-X.Y.0标签和release/framework-X.Y维护分支,两者都精确指向 pin 的发布来源 commit。这里的create_or_verify_ref函数特意做成"重试安全":若 ref 已存在,校验其指向的 SHA 与发布来源一致则继续,不一致则报错——避免重试发布时产生指向错误 commit 的 tag。
  2. 发布 Python wheel 和 sdist:先按 commit 地址从artifact.flower.ai/framework/commits/<SHA>/下载预构建的flwr-X.Y.0-py3-none-any.whlflwr-X.Y.0.tar.gz(第 45 行起),复制到 S3 的py/release/vX.Y.0/稳定路径(第 87 行起),再用uv publish发布到 PyPI(第 105 行起)。
  3. 提升(promote)预构建 Docker 镜像到稳定标签(第 138 行起):读取digests.json中记录的每个镜像的不可变索引摘要(index_digest),用docker buildx imagetools create为其追加稳定标签而不重新构建。标签映射规则在脚本中可见:unstable-*变体标签会派生出带版本号的稳定标签,其中superlink/supernodepy3.13-alpine3.22变体获得纯版本号标签X.Y.0py3.13-ubuntu24.04变体获得latest标签,superexec的 ubuntu 变体同时获得两者。这一策略与 update_version.py 中维护的 Docker README 标签组定义相互对应。
  4. 从维护分支触发文档构建(第 175 行起):执行gh workflow run framework-docs.yml --ref release/framework-X.Y,即文档是从新建的维护分支而非main构建的,保证发布版文档对应发布版代码。
  5. 发布 GitHub Release(第 182 行起):把framework/docs/source/changelog/vX.Y.0.md去掉首行版本标题和空行(sed '1,2d')作为 release notes,用gh release create framework-X.Y.0创建发布,并把 wheel 和 sdist 作为附件上传。

为什么 finalize 不重新构建:commit 粒度的预构建

文档最后一段解释了一个关键设计:发布产物并不是在发布时现场构建的,而是由 framework-commit-artifacts.ymlmain的每次 push 时预先构建("built ahead of time")。该 workflow 的具体做法(可对照源码核对):

  • package-distributions作业在main每次推送后运行:先判断本次推送是否触及 Docker 相关路径(framework/py/framework/pyproject.tomlframework/docker/等),然后执行uv run --no-sync ./dev/build.sh构建 wheel 与 sdist、跑dev/test-wheel.sh测试产物,最后把framework/dist/整体上传到 S3 的framework/commits/<本次SHA>/路径(第 79 行起)。
  • 若本次推送没有 Docker 相关变更,则直接复用上一个 commit 的digests.json镜像索引,仅更新其中的commit_sha字段(第 92 行起);有变更时才走完整的镜像矩阵构建:build-docker-image-matrix.py 生成构建矩阵,base 镜像与 binary 镜像分别构建,最终把每个镜像的repository/tags/index_digest汇总成digests.json存回同一 commit 目录(第 203 行起)。

因此 finalize workflow 的职责被简化为"把精确 pin 的发布来源 commit 的已有产物提升到稳定位置"(promote),而不是在发布窗口内重新构建。这带来两个直接收益:发布动作快且失败面小;发布出去的包、镜像与main上被 CI 测试过的那份字节级产物完全一致。同时,"pin commit + 预构建产物 + 合并后提升"三者共同保证了发布的可追溯性——framework-X.Y.0tag、PyPI 上的包、镜像的 digest、GitHub Release 的附件,全部指向同一个release_source_sha

操作要点速查

  • 唯一人工入口gh workflow run framework-release-prepare.yml --repo flwrlabs/flower -f version=X.Y.0(或 Actions 网页端触发 "Framework Prepare Minor Release"),版本必须形如1.34.0
  • 重复触发是安全且推荐的:main 有新提交后用同一版本再触发一次,发布 PR 会被刷新并重新 pin 到 main HEAD。
  • 唯一人工审查点framework/docs/source/changelog/vX.Y.0.md,审查、编辑、批准、合并,四步即结束人工流程。
  • 绝对不做的事:手动打 tag、手动发 PyPI 包、手动推 Docker 镜像、手动建 GitHub Release。
  • 排查发布问题时按链路查:prepare(framework-release-prepare.yml)→ check(framework-release-check.yml)→ finalize(framework-release-finalize.yml),状态文件 .github/framework-release.json 记录了当前版本的发布来源 SHA,是定位"发出去的到底是不是我 pin 的那个 commit"的第一现场。

【免费下载链接】flowerFlower: A Friendly Federated AI Framework项目地址: https://gitcode.com/GitHub_Trending/flo/flower

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询