Beads 版本发布流程全指南:从 release 脚本到多渠道自动化发布

原创2026-09-12 23:18:121,710 阅读
文章标签:缺陷追踪项目管理CLI开发工具Agent 记忆

Beads 版本发布流程全指南:从 release 脚本到多渠道自动化发布

本指南以 Beads 开源仓库中的发布流程文档为核心,完整讲解如何为 Beads 发布一个新版本:从推荐的一键发布脚本、手动分步流程、版本号同步机制,到 GitHub Releases、Homebrew、PyPI(MCP 服务器)等多渠道的发布与验证。读完本文,你将掌握 Beads 完整的版本发布链路、底层脚本与 CI 自动化的实现原理,并能独立完成一次从版本升级到渠道验证的正式发布。

发布总览:Beads 的多分发渠道

Beads(CLI 工具名为 bd)是一个面向编码 Agent 的内存升级工具,其发布不是单一的二进制上传,而是覆盖多个分发渠道的协同过程:

  1. GitHub Release —— 面向所有平台(Linux / macOS / Windows,amd64 / arm64)的二进制下载;
  2. Homebrew —— macOS / Linux 的包管理器分发(brew install beads);
  3. PyPI —— Python 生态的 MCP 服务器包 beads-mcp(位于 integrations/beads-mcp);
  4. npm —— 面向 Node.js 环境的包装包 @beads/bd(位于 npm-package);
  5. 插件市场元数据 —— Claude / Codex 插件及市场清单的版本号;
  6. 文档站 —— CLI 文档与发布 pin(docs/cli-docs.pin)的同步。

发布流程文档位于仓库 engdocs/staged-for-removal/RELEASING.md,根目录下另有内容更完整的 RELEASING.md 可作为交叉参考;脚本使用说明见 scripts/README.md。

推荐方式:一键发布脚本

对于常规发布,Beads 提供了"一键发布"的自动化脚本:

./scripts/release.sh 0.9.3

该脚本会完成一切:版本升级、测试、git tag、Homebrew 更新、本地安装。从源码看(scripts/release.sh),它实际上是一个"发布分子(release molecule)的网关"——它调用 bd mol wisp beads-release --var version=<版本> 创建一个带步骤清单、可观测、可断点续传的发布工作流,而不是传统意义上一次性跑完的批处理脚本。

脚本内部的选择逻辑也值得注意:

  • 若环境变量 BD 已设置且其指向的 bd 能解析仓库内的 beads-release formula,则优先使用;
  • 否则回退到 PATH 中的 bd,再到仓库根目录编译出的 ./bd;
  • 最后兜底使用 go run -tags gms_pure_go ./cmd/bd(源码见 scripts/release.sh)。

常用的两个调用形式:

# 完整发布(做所有事情)
./scripts/release.sh 0.9.3

# 预演:只显示将要发生什么,不产生任何实际变更
./scripts/release.sh 1.0.0 --dry-run

脚本完成后的自动化接力:

  • GitHub Actions 构建全平台二进制(约 5 分钟);
  • PyPI 包自动发布;
  • Homebrew core formula 被验证或跟踪其正式 PR;
  • GitHub Release 附带二进制与 changelog 自动创建。

为什么用"发布分子"而不是脚本

scripts/release.sh 头部注释解释了这一设计决策:Orchestrator(编排器)在多步骤工作流中使用 molecule 而非批处理脚本,原因有四:

  1. 账本(LEDGER):每个步骤都是工作历史中的一条带时间戳记录,脚本则完全不可见;
  2. 可续跑(RESUMABILITY):发布在第 17 步失败时,可从第 17 步继续,脚本则必须从头再来;
  3. 可观测(OBSERVABILITY):bd activity --follow 可实时查看进度,脚本在结束前是黑盒;
  4. 门禁(GATES):发布分子通过门禁等待 CI 完成,而不是轮询式忙等。

此外,脚本明确警告不要用 gh run watch 或循环轮询 CI 状态——GitHub API 每小时仅 5000 次请求,历史上多次发布因轮询耗尽整个配额、阻塞全体成员长达一小时。等待 CI 应交由分子门禁系统处理。

手动方式:逐步控制

若偏好逐步控制,Beads 也提供了完整的手动发布流程。

预发布检查清单

1. 停止所有正在运行的 Dolt 服务器(关键步骤)

# 停止所有工作区中的 Dolt 服务器
bd dolt stop

# 或按进程查找并停止
pkill -f "dolt sql-server" 2>/dev/null
pgrep -lf "dolt sql-server" || echo "No Dolt servers running ✓"

为什么这很重要:旧版本服务器会引发三类问题——

  • 自动刷新(auto-flush)竞态条件,导致提交后工作树仍然脏;
  • 客户端(新版)与服务器(旧版)之间的版本不匹配;
  • 变更看似同步错误的神秘行为。

2. 运行测试与构建

make test
golangci-lint run ./...
make build
./bd version  # 验证显示新版本号

3. 跳过本地安装(避免 go install 与 brew 冲突)

  • 直接使用仓库中的 ./bd 进行测试;
  • 系统级 bd 将在 Homebrew formula 更新后通过 brew 更新;
  • 如需临时使用,可 alias bd="$PWD/bd"。

4. 更新 CHANGELOG.md

  • 添加版本标题:## [0.9.X] - YYYY-MM-DD;
  • 按 Added、Fixed、Changed、Performance、Community 分类汇总变更;
  • 更新 Version History 章节;
  • 如有需要,添加 Upgrade Guide 章节。

5. 提交 changelog

git add CHANGELOG.md
git commit -m "Add 0.9.X release notes"

版本升级(Version Bump)

手动流程使用自动化脚本更新所有版本文件:

./scripts/bump-version.sh 0.9.X --commit
git push origin main

从源码看,scripts/bump-version.sh 目前已被标记为 DEPRECATED(已弃用)——它现在只是一个提示脚本,引导用户改用发布分子 bd mol wisp beads-release --var version=X.Y.Z,或使用纯本地版本升级脚本 scripts/update-versions.sh。这体现了发布体系从"手工脚本"向"分子编排"的演进。

当前实际负责版本同步的是 scripts/update-versions.sh,它会更新以下文件(较旧文档的清单更完整):

文件 说明
cmd/bd/version.go bd CLI 版本常量(当前仓库为 Version = "1.2.2",见 cmd/bd/version.go)
plugins/beads/.claude-plugin/plugin.json Claude 插件版本
plugins/beads/.codex-plugin/plugin.json Codex 插件版本
plugins/beads/.copilot-plugin/plugin.json Copilot 插件版本
.claude-plugin/marketplace.json Claude 市场版本
integrations/beads-mcp/pyproject.toml MCP 服务器版本
integrations/beads-mcp/src/beads_mcp/__init__.py MCP Python __version__
npm-package/package.json npm 包版本
README.md README 中的 Alpha 版本徽章
default.nix Nix 打包版本
.githooks/* 托管 git-hook 的 BEGIN/END BEADS INTEGRATION vX.Y.Z --- 标记
cmd/bd/winres/winres.json、cmd/bd/winres/manifest.xml Windows PE 资源元数据

脚本还会同步 integrations/beads-mcp/uv.lock(MCP 锁文件),并支持 prerelease 标识符(如 1.1.0-rc.1)。Windows PE 的数字字段(file_version / product_version / manifest 版本)只写入不含后缀的基础版本号,因为 PE 版本必须是纯数字——构建时 scripts/gen-winres.sh 也会以同样方式剥离 prerelease 后缀。

重要:版本升级后必须重新构建本地二进制:

make build
./bd version  # 应显示新版本

版本一致性校验

scripts/check-versions.sh 是发布前必须通过的校验门:它以 cmd/bd/version.go 中的版本为唯一权威(canonical)来源,逐一比对 MCP pyproject.toml、__init__.py、Claude/Codex 插件 JSON、市场清单、npm package.json、MCP uv.lock(按 PEP 440 规范化后比较,1.1.0-rc.1 → 1.1.0rc1)以及 .githooks/* 标记版本,任何不一致都会以非零退出码失败。

锁文件过期曾付出过真实代价:uv sync --locked 只在 tag 触发的发布运行中失败,而 tag 已存在无法重写——这曾烧掉 v1.1.1 tag,并让 v1.1.0 与 v1.1.2 的首次发布运行失败。因此现在版本升级脚本会主动刷新锁文件,check-versions.sh 也会在 tag 创建前就拦住过期锁。

发布到所有渠道

1. 创建 Git Tag

git tag v0.9.X
git push origin main
git push origin v0.9.X

推送 tag 后,GitHub Actions 会自动处理其余部分:

  • GoReleaser 构建二进制并发布到 GitHub Releases;
  • PyPI 发布任务上传 MCP 服务器包到 PyPI。

发布自动化只对 v* tag 生效。应保持 refs/tags/v* 受保护,只有可信的发布维护者才能创建、更新或删除发布 tag。手动触发工作流只能从 tag 重跑发布,而不是从分支。

2. GitHub Secrets 设置(一次性)

自动化需要一个密钥:

PYPI_API_TOKEN:在 PyPI 账户管理页生成 API token,添加到仓库的 Actions secrets 设置中,命名为 PYPI_API_TOKEN,值为完整的 pypi-... token。

3. 手动 PyPI 发布(如自动化失败)

cd integrations/beads-mcp

# 清理并重建
rm -rf dist/ build/ src/*.egg-info
uv build

# 上传到 PyPI
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-... uv tool run twine upload dist/*

详细的 PyPI 指南见 integrations/beads-mcp/PYPI.md。核心要点:

  • 构建产生 wheel(dist/beads_mcp-X.Y.Z-py3-none-any.whl)与源码包(dist/beads-mcp-X.Y.Z.tar.gz);
  • 先上传 Test PyPI 验证,再传生产 PyPI(twine upload --repository testpypi dist/*);
  • 认证统一使用 __token__ 作为用户名、完整 token 作为密码;
  • 发布后在全新虚拟环境中 pip install beads-mcp 验证 beads-mcp --help 与 python -c "import beads_mcp; print(beads_mcp.__version__)"。

4. 验证 Homebrew 更新

Homebrew formula 位于 homebrew-core,随 GitHub Release 产物自动更新:

brew update
brew upgrade beads  # 或: brew install beads
bd version  # 应显示 v0.9.X

注意:如果 PATH 中残留旧的 go install 安装的 bd 二进制,需要移除以避免冲突:

# 查找 bd 安装位置
which bd

# 若在 Go 工具链路径中(如 ~/go/bin/bd 或 mise 管理的 Go),移除它
# 常见位置:
rm ~/go/bin/bd                                  # 标准 go install 位置
rm ~/.local/share/mise/installs/go/*/bin/bd     # mise 管理的 Go 安装

# 验证使用的是正确版本
which bd        # 应显示 /opt/homebrew/bin/bd 或包管理器路径
bd version      # 应显示最新版本

有趣的是,bd version 命令本身会检测 PATH 中的重复二进制并给出警告(见 cmd/bd/version.go 的 findDuplicateBinaries),这正是多安装来源冲突的常见场景。

5. GitHub Releases(自动化)

推送 tag 后 GoReleaser 自动创建 release。.github/workflows/release.yml 工作流:

  • 由 v* tag 触发;
  • 构建跨平台二进制(Linux、macOS、Windows 的 amd64/arm64);
  • 生成校验和(checksums);
  • 用二进制与 changelog 创建 GitHub Release;
  • 自动发布。

只需推送 tag 并等待约 5 分钟:

git push origin v0.9.X

tag 工作流在发布前会重跑关键发布包门禁:

  • make ci-package-mcp:构建并校验 MCP 包,PyPI 任务发布的是该门禁产生的已验证 dist/* 产物;
  • make ci-package-npm:在 npm 发布前校验 npm 包装包。

npm 发布任务还会等待 macOS 发布产物就绪,因为 npm 包的 postinstall 脚本需要从 GitHub Release 下载平台专属归档。

6. 文档站同步

发布流程文档提到文档站是版本化的:未发布的编辑位于 website/docs/(标记为 Next),每次发布都应生成快照:

cd website
npm ci
npm run docusaurus docs:version X.Y.Z

然后在 website/docusaurus.config.ts 中设置 lastVersion 为 X.Y.Z,让访客默认看到最新稳定版文档而非 Next。随发布提交 website/versioned_docs/、website/versioned_sidebars/ 与 website/versions.json。

需要注意的是,仓库现状中文档体系已进一步演进:根目录 RELEASING.md 说明当前文档站为 Mintlify(docs/),随 main 分支自动部署,无需发布时快照;但 发布 pin 是发布时的必需步骤——docs/cli-docs.pin 记录了文档语料生成与校验所依据的发布 tag:

# tag 之后执行:
echo "v0.22.0" > docs/cli-docs.pin
./scripts/generate-cli-docs.sh
git add docs/cli-docs.pin docs/CLI_REFERENCE.md docs/cli-reference docs/docs.json
git commit -m "docs: bump CLI docs pin to v0.22.0"
git push origin main

跳过此步不会快速失败——scripts/check-doc-flags.sh 的第 4 项检查会基于旧 pin 保持绿色,直到下次 bump 时,跨越的多次发布中新增的所有命令会一次性堆积成大量 "missing from docs/CLI_REFERENCE.md" 失败,且难以定位是哪次发布导致的。

预发布(RC)工作流

发布候选(RC)让构建经过完整发布管线验证,但不推送到稳定渠道。RC 携带 SemVer 预发布标识符(如 1.1.0-rc.1);Python 工具链会将其规范化为 PEP 440 形式(1.1.0rc1)。

预发布 tag 与稳定发布的关键差异:

  • GitHub Release 发布并被标记为 prerelease(GoReleaser release.prerelease: auto),含全平台二进制;
  • Homebrew 不更新(GoReleaser brews.skip_upload: true,core formula 只跟踪稳定发布);
  • PyPI 与 npm 发布任务被跳过——publish-pypi 和 publish-npm 任务以 !contains(github.ref_name, '-') 为门,含 - 的 tag 永远不会进入稳定包渠道;
  • 文档不受影响。

切一个 RC:

# 1. 更新 CHANGELOG.md 与 cmd/bd/info.go 的 RC 说明(手动步骤),为 CHANGELOG 章节标注日期

# 2. 升级版本,update-versions.sh 接受预发布标识符
./scripts/update-versions.sh 1.1.0-rc.1
#    Windows PE 数字字段(winres file_version/product_version 及 manifest
#    <assemblyIdentity> 版本)设为基础版本 1.1.0,因为 PE 版本必须为纯数字

# 3. 保持 MCP 锁文件同步(PEP 440 规范化为 1.1.0rc1),否则 Package Gate (MCP) 检查变红
(cd integrations/beads-mcp && uv lock)

# 4. 本地校验
./scripts/check-versions.sh

# 5. 为 RC 准备打开 PR 并评审,RC 准备应走正常评审而非自动合并

RC 准备合并到 main 后,从合并提交切 tag:

git checkout main && git pull
git tag -a v1.1.0-rc.1 -m "Release candidate v1.1.0-rc.1"
git push origin v1.1.0-rc.1

验证与提升(promote):从 GitHub prerelease 产物安装 RC 并充分验证;要提升为稳定版,用不带后缀的基础版本 bump(./scripts/update-versions.sh 1.1.0),稳定发布会真正推送 Homebrew/PyPI/npm。

热修复与回滚

热修复发布

# 从 tag 创建热修复分支
git checkout -b hotfix/v0.22.1 v0.22.0

# 进行修复
# ... 编辑文件 ...

# 升级版本到 0.22.1
./scripts/bump-version.sh 0.22.1 --commit

# 打 tag 并发布
git tag -a v0.22.1 -m "Hotfix release v0.22.1"
git push origin hotfix/v0.22.1
git push origin v0.22.1

# 遵循正常发布流程
goreleaser release --clean

# 合并回 main
git checkout main
git merge hotfix/v0.22.1
git push origin main

回滚流程

若发布存在严重问题:

  1. 将 GitHub Release 标记为 prerelease:
    gh release edit v0.22.0 --prerelease
    
  2. 创建热修复版本:按上述热修复流程发布 0.22.1;
  3. 弃用 npm 包(如需):npm deprecate @beads/bd@0.22.0 "Critical bug, please upgrade to 0.22.1";
  4. Yank PyPI 版本(如需):PyPI 不允许删除,但可以 yank(从 pip install 隐藏)。

版本号规则与发布节奏

Beads 遵循语义化版本(Semantic Versioning):

  • MAJOR(x.0.0):破坏性变更;
  • MINOR(0.x.0):向后兼容的新功能;
  • PATCH(0.0.x):向后兼容的缺陷修复。

示例:0.21.5 → 0.22.0 为小版本(新功能);0.22.0 → 0.22.1 为补丁(缺陷修复);0.22.1 → 1.0.0 为大版本(稳定发布)。预发布追加 SemVer 预发布标识符(如 1.1.0-rc.1),只发布 GitHub prerelease,不进入稳定 Homebrew/PyPI/npm 渠道。

发布节奏:小版本每 2-4 周;补丁按需发布;大版本在出现破坏性变更时发布。

发布后操作

  1. 停止旧 Dolt 服务器,确保本地立即使用新版本:
    bd dolt stop
    pkill -f "dolt sql-server" 2>/dev/null
    pgrep -lf "dolt sql-server" || echo "No Dolt servers running ✓"
    
  2. 验证安装:
    # Homebrew
    brew update && brew upgrade beads && bd version
    
    # PyPI
    pip install --upgrade beads-mcp
    beads-mcp --help
    
  3. 发布公告(可选):项目 Discord/Slack、社交媒体、README 徽章。

排障指南

过期的 dist/ 目录

上传前始终 rm -rf dist/,避免上传旧版本。

PyPI 版本冲突

PyPI 不允许重复上传同一版本。即使只是修复也要递增版本号。

Homebrew SHA256 不匹配

推送 tag 后等待几秒,让 GitHub 生成 tarball,再重新计算 SHA256。

缺少 PyPI 凭据

在 PyPI 账户管理页设置 API token,并使用 __token__ 作为用户名。

"Tag already exists"

git tag -d v0.22.0
git push origin :refs/tags/v0.22.0
git tag -a v0.22.0 -m "Release v0.22.0"
git push origin v0.22.0

"npm publish fails with EEXIST"

版本已发布过,执行 npm version patch 后重新 npm publish。

npm postinstall 二进制下载失败

确保 GitHub Release 先发布,检查二进制 URL 正确,确认 package.json 与 GitHub Release 中的版本一致。

GoReleaser 构建失败

goreleaser check        # 检查 .goreleaser.yml 语法
goreleaser build --snapshot --clean   # 本地测试构建

自动化状态一览

已自动化:

  • GitHub Releases 含二进制(GoReleaser + GitHub Actions);
  • PyPI 发布(经 GitHub Actions 自动化);
  • 跨平台构建(Linux、macOS、Windows);
  • 校验和与 changelog 生成;
  • Windows 二进制签名(scripts/sign-windows.sh,经 GoReleaser 自动调用,未配置签名密钥时优雅降级为不签名)。

TODO:

  • 自动更新 Homebrew formula(当前由 GitHub Release 产物驱动 homebrew-core 更新)。

相关文档

登录后查看全文
beads