Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

发布运行手册

临时手动流程。 本操作手册介绍如何使用 release-stable-manual.yml 在今日发布稳定版本。在 release-plz 落地并取代它之前,这仍是当前生效的流程;该迁移尚未发生(参见未来规划)。

如果这里有任何内容让人觉得繁琐,那是有意为之的阻力,我们尚未具备能够安全移除它的自动化规范能力。

最后针对 v0.8.2 发布周期进行验证。


七步流程

  1. 使用 changelog 技能生成 CHANGELOG-next.md,并包含二进制文件大小表
  2. 打开并合并版本升级 PR
  3. 使用 act 在本地干运行发布工作流
  4. 通过手动派发触发 Release Stable 工作流
  5. 在提示时批准这两个环境门禁
  6. 验证发布是否存在且资产可下载
  7. 版本化文档部署

这就是完整流程。其他所有操作(crates.io、Docker、网站重新部署、Scoop、AUR、Discord、推文)都会作为下游作业自动运行。Homebrew Core 会通过其自身的自动更新服务检测稳定版 GitHub 发布。除非某个作业明确失败,或者 Homebrew 的外部更新仍然滞后,否则你无需对这些操作做任何处理。


步骤 1:生成 CHANGELOG-next.md

运行 changelog-generation 技能以生成 CHANGELOG-next.md。其完整流程位于 .claude/skills/changelog-generation/SKILL.md。

该技能会根据上一个稳定标签与 HEAD 之间的 git log 生成更新日志,通过 GitHub GraphQL 解析贡献者信息,并写入文件。可将结果直接提交到一个短期分支,并将其纳入版本号升级 PR(步骤 2)中;如果差异较大,则将其作为单独的前置 PR 提交。

如果 CHANGELOG-next.md 因之前中止的发布周期而已存在,请在重新使用前检查其准确性。

该文件必须在前言之后直接包含一个 ## In brief 部分:两段简短文字,合计不超过 255 个字符,说明本次发布的内容。X 和 Discord 的公告工作流会发布该部分,随后是前言中的提交数和贡献者数,以及指向官网发布文章的链接(若尚无发布文章,则链接到 GitHub Release)。若缺少该部分,工作流会回退到 Highlights 的前几条要点;若也没有这些要点,则回退到原始提交列表。该技能的风格规则和参考示例位于 SKILL.md 中。

预览任何已发布标签的公告文本,但不发布:

gh workflow run tweet-release.yml -f release_tag=vX.Y.Z -f dry_run=true
gh workflow run discord-release.yml -f release_tag=vX.Y.Z -f dry_run=true

撰写的文本会显示在本次运行的作业摘要中。

记录二进制文件大小的变化

在升级版本号之前,测量上一个版本和当前 master 的发布二进制文件,然后将 compare 表复制到 CHANGELOG-next.md 中,作为 Contributors 之前的 ## Binary size 一节,并置于 text 代码块内。CHANGELOG-next.md 会成为发布正文,因此该表会随版本一同发布。该表报告的是在同一台机器上对两个源码修订版本进行的本地构建。它不测量已发布的归档包(这些归档包由发布工作流针对每个目标平台、使用其固定的工具链和目标标志构建),因此请在表旁注明测量所用的目标三元组。

scripts/ci/binary_size_report.sh 可在 Linux 或 macOS 上运行,需要 Python 3.11 或更高版本。它的 measure 命令会使用 release Cargo 配置文件,为 dev/ci/dependency-footprint.toml 中每个 zeroclaw 策略配置构建 zeroclaw 二进制文件。对于每个策略配置,报告会记录二进制文件的字节数、SHA-256 和路径、目标三元组以及解析后的特性。报告还会记录 Cargo.toml 中的 release Cargo 配置文件设置、Cargo 和 rustc 版本、会影响代码生成的构建变量(RUSTFLAGS、CARGO_ENCODED_RUSTFLAGS、CARGO_BUILD_RUSTFLAGS、目标的 RUSTFLAGS 和链接器变量,以及 ZEROCLAW_BUILD_ID)、带有工作树摘要的源码修订版本,以及 Cargo.lock 摘要。

在同一台机器和同一用户账户下,分别从各自干净的检出目录中测量每一侧,并将报告保存在两个检出目录之外,因为检出目录中的未跟踪文件会使其下一次测量被标记为脏状态:

git worktree add ../zeroclaw-vPREV vPREV
./scripts/ci/binary_size_report.sh measure --repo-root ../zeroclaw-vPREV \
  --profile foundation --profile agent-runtime \
  --profile root-default --profile standard-distribution \
  --target-dir ../zeroclaw-vPREV/target-size --output ../size-vPREV.json
./scripts/ci/binary_size_report.sh measure \
  --profile foundation --profile agent-runtime \
  --profile root-default --profile standard-distribution \
  --target-dir target-size --output ../size-next.json
./scripts/ci/binary_size_report.sh compare ../size-vPREV.json ../size-next.json \
  --output ../size-compare.json

这些命令用于测量四个策略配置文件(profile),它们描述了用户实际运行的构建方式:foundation(--no-default-features)、agent-runtime、root-default(源码构建时获得的 Cargo 默认特性),以及 standard-distribution(发布归档包的特性集)。若不指定 --profile,measure 会构建所有 zeroclaw 策略配置文件,包括 ci-all 和 hardware-probe。这两者是 CI 和硬件的汇总配置,而非实际发布的构建。传入 --target <triple> 可为其他目标平台构建。若不传入,measure 会为主机三元组解析 standard-distribution 的特性,因此 Cargo.toml 中针对各目标平台的排除规则,与该目标发布归档包所适用的规则保持一致。

compare 会打印表格,表头为两侧各自的修订版本及其是否为脏工作区(dirty)状态;同时将 JSON 结果写入 --output 指定的文件,若未指定该参数,则输出到标准输出(stdout)。

二进制文件会嵌入 CARGO_HOME 下的注册表源路径,以及来自 git describe 或 ZEROCLAW_BUILD_ID 的构建标识。因此,同一份源码在另一台机器或另一个用户账户上测量,可能生成不同的字节内容,而 compare 无法检测出这类差异。其他未被记录的输入也会改变二进制文件,包括 C 编译器变量(如 CC 和 CFLAGS)、macOS SDK 和部署目标版本,以及用户级 ~/.cargo/config.toml 中的 Cargo 设置。请在同一台机器、同一用户账户、相同的 shell 环境下对两侧进行测量。

measure 在设置了 RUSTC 或 CARGO_BUILD_RUSTC 时拒绝开始构建,在 CARGO_PROFILE_RELEASE_* 变量覆盖了 release Cargo 配置文件时拒绝开始构建,在所选策略配置文件不构建 zeroclaw 包时拒绝开始构建,或者在策略配置文件的目标目录位于检出目录内但未被 Git 忽略时拒绝开始构建。每次构建后,如果 Cargo 报告的二进制文件路径与预期不同(例如 CARGO_BUILD_TARGET 或 build.target 设置在未使用 --target 的情况下生效),它会拒绝该二进制文件。compare 拒绝比较在 Cargo 或 rustc 版本、主机、目标、release Cargo 配置文件设置、已记录的构建变量、策略或策略配置文件集合上存在差异的报告。如果某个策略配置文件在两份报告之间解析后的特性或二进制文件路径发生了变化,它会被列为不可比较,并列出新增和移除的特性,而不会生成差值。

每个策略配置文件都会在其自己的目标目录 <target-dir>/<profile-id> 中构建,这会为每个策略配置文件重复依赖项的构建。在 macOS arm64 上,这些目录分别占用 foundation 0.8 GB、agent-runtime 1.2 GB、root-default 1.4 GB 和 standard-distribution 2.5 GB。策略配置文件的首次构建是完整的 fat-LTO release 构建,耗时数分钟;在源码未变更的情况下重复运行则无需重新构建即可完成。当该表格写入变更日志后,请进行清理:

rm -rf target-size
git worktree remove ../zeroclaw-vPREV

对于完整的目标矩阵,Cross-Platform Build 工作流的作业摘要会列出每个二进制文件的字节数和 SHA-256。


步骤 2:升级版本并合并版本 PR

在工作区 Cargo.toml 中提升 workspace.package.version,然后按顺序运行这两个发布脚本。首先同步仓库中所有版本引用:

sh

./scripts/release/bump-version.sh --release    Cargo.toml 中的版本

此更新会修改 README 徽章、Tauri 配置和工作流描述示例,然后通过 cargo generate installers 重新生成所有由规范驱动的安装相关内容:install.sh、setup.bat、dist/aur/PKGBUILD、dist/aur/.SRCINFO、dist/scoop/zeroclaw.json、flake.nix、Dockerfile/Containerfile 功能集、dev/ci/docker-tags.toml、docs/book/src/_snippets/install.md、README/平台文档中的 Unix 快速路径区块,以及 docs/book/src/setup/windows.md 中的 Windows 预构建版本区块。版本、功能和应用打包相关值来自 Cargo.toml 和 [package.metadata.zeroclaw];四种稳定安装路径来自 xtask/src/generate/spec.rs 中的类型化契约。此次变更会自动使这些内容保持同步,因此绝不要手动编辑生成区域。实时版本发布可用性仍由人工维护,不会由生成器推断。此脚本还会通过 scripts/dev/refresh-nix-hashes.sh 刷新 Nix Git 依赖项哈希值(nix/hashes.json)。

发布模式(--release)在编辑任何文件之前,需要具备以下条件:cargo、jq、nix-prefetch-git、perl、sha256sum、带有 tomllib 的 Python 3.11+、PATH 中可用的 Bash 4+、锁文件以及 Nix 刷新脚本。在 macOS 上,请安装较新版本的 Bash,并将其置于 PATH 中 /bin/bash 之前,以供 Nix 刷新程序使用。若锁文件解析、Nix 哈希计算或安装程序生成失败,脚本会停止运行。失败时,工作区中可能保留之前的编辑:请检查这些修改,修复所报告的前置条件后重新运行。不要提交不完整的版本升级。若不使用 --release,脚本对本地准备工作保持尽力而为(best-effort)的行为。

打 tag 的辅助脚本 scripts/release/cut_release_tag.sh 同样使用发布模式。准备阶段失败时,它会在提交、拉取、打 tag 或推送之前停止;普通本地模式不能作为打 tag 的回退方案。

刷新并固定翻译

在 bump-version.sh 设置发布版本后,刷新文档翻译目录并将其固定到对应的标签。如果目录是单独准备的,请在打标签前检查覆盖率并对其进行验证:

cargo mdbook stats
cargo mdbook check

然后运行发布封装器:

sh

./scripts/release/refresh-translations.sh --model-provider anthropic.release

refresh-translations.sh 从 Cargo.toml 读取版本号(无需手动输入),执行翻译流程,将目录文件提交并推送到 zeroclaw-labs/zeroclaw-docs-translations 子模块,在该子模块处打 v{version} 标签,并将主仓库的 gitlink 固定到该标签后暂存。若子模块尚未检出,则自动初始化。请在 bump-version.sh 之后运行本脚本,以确保其读取的 Cargo.toml 版本号为发布版本。配置的 provider 别名须显式传入,以免发布依赖硬编码的后端;必要时传入 --config-dir。--model-provider 所选别名从 providers.models.<kind>.<alias> 解析。若目录文件已是最新状态,可使用 --no-translate;也可在 --model-provider 之前传入显式版本号以覆盖 Cargo.toml 中的默认值,例如:

./scripts/release/refresh-translations.sh 0.8.2 --model-provider anthropic.release

一起提交所有内容:

chore: bump version to vX.Y.Z

如果 PR 还更改了 [workspace.package] rust-version 或固定的 Rust 工具链,请将其视为兼容性变更,而不只是发布流程调整。PR 应当注明新的 MSRV,说明源码构建的升级路径,并在合并前展示 CI、Docker、安装程序和生成的各个面向都与新的最低版本保持一致。

提交一个 PR。为其添加 type:ci、size:XS 标签,以及 PR 标签器添加的所有路径标签。如果该 PR 提高了工具链下限,还要添加 risk:high,并通过通道 D 处理。

两名独立的 Core Team 审批是默认要求。工具链下限和发布相关变更,只有在提供完整证据并明确说明其属于通常需要两次审查的类别的理由时,才可依据加急二次审查通道予以考虑。超时绝不能消除尚未解决的兼容性或发布安全问题。只有在必需的 CI 全部通过后才可合并。

Installer Drift 门禁会在 CI 中检查生成的产物是否与规范保持同步。如果某个生成产物与规范不一致,PR 将失败,因此遗漏的重新生成无法合并。Validate Translations Pin 门禁会在固定的提交上解析子模块,并校验翻译目录的格式与 msgid 一致性,因此错误的 pin 同样无法合并。翻译流程的详细信息请参阅 文档与翻译。

crates.io Package Preflight 门禁会在 PR 将 [workspace.package] version 修改为稳定版 X.Y.Z 时运行。它会打包发布集中的每个 crate,并从各自的 tarball 单独编译,不需要 registry token,因此无法发布的版本升级不能合并。预计会为版本升级 PR 增加约半小时。它也会在合并队列中再次运行。如果失败,请先阅读 Preflight 步骤。打包或编译错误属于真实问题,必须在版本升级 PR 中修复。crates.io API 错误、npm 安装失败或 runner 超时属于基础设施问题。只需重新运行失败的任务,无需再次推送。如果故障持续,请在必需的 CI 变为绿色之前保持版本升级不合并。发布运行会在 GitHub Release 之前独立地重复相同的 preflight。

确认合并已正确落地:

sh

git fetch origin
git show origin/master:Cargo.toml | grep '^version'
# 必须显示:version = "X.Y.Z"

步骤 3:使用 act 在本地试运行发布工作流

Release Stable 工作流是一个 GitHub Actions 作业图,它会在你点击 Run workflow 的那一刻消耗掉你的环境门控审批窗口。如果某个工作流步骤出现问题:缺失构建产物、过时的路径、某人在未更新 CI 的情况下移除了代码生成步骤,那么这些故障会在你已经投入到一个发布窗口_之后_才暴露出来,此时版本 PR 已经合并,master 已经处于新版本。恢复意味着要落地一个紧急修复分支、重新运行 CI,并在一棵已经把自己标榜为完全发布版本的代码树上、在时间压力下完成发布。

针对这种情况,廉价的保险措施是:在打开 GitHub Actions 表单之前,先在本地、针对完全相同的合并后 master 提交,运行同一份作业图。act 使用与 GitHub 相同的 actions/* 生态系统,在 Docker 容器内执行 GitHub Actions 工作流。它并不能完美地镜像云端 runner;它无法访问构件上传运行时、GitHub 签发的 OIDC 令牌、环境密钥,也无法运行那些依赖真实发布标签的作业,但它确实能运行构建和测试步骤,而我们曾遇到的几乎所有发布时 CI 失败都源于这些步骤。

这一步骤每次发布需投入 15–20 分钟。它发现了常规的逐 PR CI 未能暴露的真实缺陷(因为失败的工作流仅在 workflow_dispatch 时运行,而非在 push 时运行)。

一次性设置

act 运行工作流。最简洁的安装方式是使用 GitHub CLI 扩展,因为它会继承你的 gh 身份验证,并向每次工作流运行暴露一个真实的 GITHUB_TOKEN:

  1. 从 https://cli.github.com 安装 GitHub CLI(Linux、macOS、Windows)。一次性认证:gh auth login。

  2. 安装 act 扩展:

    sh

    gh extension install nektos/gh-act
    

    生成工件的作业需要 act 工件服务协议,而 actions/upload-artifact v7 和 actions/download-artifact v8 都要求该协议,截至本文撰写时检查到的最新版本为止,目前发布的 act 版本中没有任何一个实现了该协议。辅助程序会在启动使用固定版本工件操作的作业前预检已安装的 act 版本,并采取故障关闭策略:不会尝试在未经验证的版本上运行该作业。在兼容的 act 版本发布并通过真实的工件往返验证之前,请对任何生成或使用工件的作业使用下面基于 GitHub 托管的回退方案;这才是目前推荐的路径,而不是罕见的例外。

  3. 从 https://docs.docker.com/engine/install/ 安装 Docker Engine 或 Docker Desktop。在 Linux 上,将你自己添加到 docker 组,这样就不需要使用 sudo。act 也可以与 Podman 和 Colima 配合使用;请参阅 act runners 文档。

这就是全部设置。仓库中的 .actrc 和 scripts/dev/act-local.sh 会处理其他所有事项(runner 镜像、密钥文件、产物服务器、action SHA 预获取)。

发布前演练

确保你的工作树与步骤 2 中合并后的 master 顶端一致:

sh

git fetch upstream
git checkout upstream/master

列出每个工作流文件中所有可运行的项:

sh

./scripts/dev/act-local.sh --list

运行特定作业、交互式选择,或运行所有可安全空运行的作业:

sh

./scripts/dev/act-local.sh release-stable-manual:web   # 一项工作
./scripts/dev/act-local.sh                              # 交互式选择器
./scripts/dev/act-local.sh --all                        # 所有安全的 dry-run 任务

首次运行会拉取 runner 镜像(约 1.5 GB),并通过 Swatinem/rust-cache 预热 Rust 构建缓存;后续运行会快得多。该脚本会自动创建被 gitignore 忽略的 .secrets 文件,将每个固定的 action SHA 预取到 ~/.cache/act/(否则 act 的浅克隆无法解析任意提交),通过父进程环境将来自 gh 认证的 GITHUB_TOKEN 传入运行过程(令牌值绝不会出现在 argv 中),并设置 --artifact-server-path,使 actions/upload-artifact 和 actions/download-artifact 能够在多个作业之间正常工作。这一切底层都只是普通的 act;该脚本只是省去了繁杂的标志参数。

在任何生成或消费工件的作业启动之前,辅助程序会将解析出的独立 act 或 gh act 版本与内部兼容性阈值(act >= 一个无法达到的哨兵值,目前为 999.0.0)进行比较。该阈值不是要通过 go install 安装的版本;没有任何已发布的 act 版本满足该阈值,只有在针对某个实际发布版本验证了真实的工件往返流程后,才会将其移至一个真实且具体的版本。所有当前已发布的 act 版本都会在构建开始前的预检阶段失败,并指向 GitHub-hosted Actions;不要为了让本地运行器通过而降低固定的工件 actions 版本。

对于 --all,会在第一个作业启动前检查整个选定作业集的兼容性。如果任何选定作业需要构件服务,则此次扫描将安全失败(当前已发布的 act 均无法达到该阈值),并退出,不运行部分作业。--all --no-allowlist 遵循相同的兼容性策略。

本地工件预检失败是当前所有已发布的 act 都会出现的预期情况,并非偶发故障。将确切的提交推送到 GitHub,并将托管工作流用作包含工件的作业的验证备用方案。只读的跨平台构建可以安全地调度,并从 CLI 进行监视:

gh workflow run cross-platform-build-manual.yml --ref <validation-branch>
gh run list --workflow cross-platform-build-manual.yml --branch <validation-branch> --limit 1
gh run watch <run-id> --exit-status

不要提前触发 release-stable-manual.yml 来替代试运行:该工作流会在其环境审批通过后发布。由于版本策略,将本地制品作业记录为已跳过,使用托管的跨平台构建完成制品往返,并将受保护的 stable-release 运行留到第 4 步。

--all 仅在“演练安全”允许列表中运行作业

act 不会遵循 GitHub 的环境保护门控。当维护者真实的 GITHUB_TOKEN 被注入到运行中时,对写入 GitHub 的作业(调用 gh release create 的 publish、推送到 GHCR 的 docker 作业、强制推送 gh-pages 的 docs-deploy、打开 issue 的 daily-audit、向 webhook 发布消息的 tweet-release 或 discord-release)进行一次成功的本地调用,就可能在首次尝试时执行真实的副作用。

因此,--all 会强制使用一份硬编码的允许列表,仅包含已证实可安全在本地运行的作业;目前包括 release-stable-manual.yml 和 cross-platform-build-manual.yml 中仅生成构件的构建步骤(validate、web、release-notes、build、build-desktop)。其他所有作业都会被跳过,并记录跳过原因:

==> skip release-stable-manual:publish (not on dry-run-safe allowlist)
==> skip release-stable-manual:docker (not on dry-run-safe allowlist)
==> skip release-stable-manual:crates (not on dry-run-safe allowlist)
==> skip release-stable-manual:redeploy-website (not on dry-run-safe allowlist)
==> skip docs-deploy:deploy (not on dry-run-safe allowlist)
==> skip daily-audit:advisories (not on dry-run-safe allowlist)
==> skip tweet-release:tweet (not on dry-run-safe allowlist)

允许列表采用**默认关闭(fail-closed)**策略:仓库中新添加的工作流会被视为可能产生变更,直到维护者审查它并将安全的作业 ID 添加到 scripts/dev/act-local.sh 中的 DRY_RUN_SAFE_JOBS。这一点很重要,因为 discover_jobs 会遍历每一个 .github/workflows/*.yml,而不仅仅是发布工作流,使用拒绝列表会悄无声息地放行未来可能存在写入操作的工作流。

在极少数情况下,如果你有理由尝试在本地运行未列入允许列表的作业,可以使用两种应急方案:

  • ./scripts/dev/act-local.sh release-stable-manual:publish:显式的 <wf>:<job> 形式会运行你所要求的内容,并在目标不在允许列表中时,于调用 act 之前打印醒目的警告。
  • ./scripts/dev/act-local.sh --all --no-allowlist:在整个 --all 运行期间禁用允许列表过滤器(仅在你已确认工作流步骤不会触及变更面时使用,例如在没有真实注册表凭据且 .secrets 文件为空的 fork 上)。

在 act 下预期会失败的情况(属于正常现象)

act 无法模拟少数仅限 GitHub 的功能。这些失败并非真正的缺陷:

  • 依赖真实发布标签的作业(publish 用于创建 GitHub Release)。
  • 环境门控的作业(publish、docker 和 crates 发布器):本地不存在审批界面。
  • 基于 OIDC 的联合身份令牌。

其他所有问题,包括 tsc 错误、缺失文件、Rust 编译失败、cargo 锁文件不匹配,都是真正的缺陷。在通过基于 master 的标准 PR 修复这些问题之前,请勿点击 GitHub Actions 表单上的 Run workflow。


步骤 4:触发发布

转到:

https://github.com/zeroclaw-labs/zeroclaw/actions/workflows/release-stable-manual.yml

点击 Run workflow。填写:

  • 分支: master
  • 要发布的稳定版本: X.Y.Z,不带 v 前缀

点击 Run workflow。

第一个作业(validate)会检查版本是否与 Cargo.toml 匹配,以及标签 vX.Y.Z 是否尚未存在。如果检查失败,请修正不匹配项并重新触发,不要尝试绕过它。


步骤 5:批准环境关卡

三个作业受 GitHub 环境保护规则限制。当每个作业进入待处理状态时,您会在工作流运行中看到 “Waiting for review” 横幅。

看到这三项时全部批准。github-releases 闸门只会在无令牌的 crates.io 预检 Preflight crates.io Workspace 完成所有 crate 的打包与编译之后出现,并且在每个 Plugin Smoke <target> 分支都已从为该目标构建的归档中安装并执行了插件之后出现。预检与二进制构建并行运行,冒烟测试分支紧随其后运行,因此若某个版本的 crate 无法发布,或其归档无法运行插件,流程会在 GitHub Release 公开之前停止。之后的 crates-io 作业会上传该预检已验证的内容,而不会重复执行验证:

环境作业它的作用
github-releasespublish创建 GitHub Release 并上传资源
dockerdocker将镜像推送到 GHCR
crates-iocrates / 发布到 crates.io按依赖顺序发布经过验证的 23-crate 工作区

如果你错过了审批窗口导致作业超时,只需从工作流运行页面重新运行失败的作业即可;无需从头重新开始。


第 6 步:验证发布

publish 完成后,请确认:

[ ] GitHub Release exists at /releases/tag/vX.Y.Z and is marked Latest
[ ] Release notes are non-empty
[ ] SHA256SUMS asset is present and non-empty
[ ] Both SPDX and CycloneDX SBOM assets are present
[ ] Exactly one zeroclaw-vX.Y.Z-verification.tar.gz asset is present
[ ] No loose *.bundle, *.attestation.jsonl, or *.intoto.jsonl assets are present
[ ] At least one binary archive is downloadable (spot-check linux x86_64)
[ ] Every Plugin Smoke leg is green; each step summary carries its check table
[ ] Prebuilt Docker and generated Docker matrix jobs are green
[ ] Release notes contain the binary size table from step 1

发布后,CHANGELOG-next.md 会被有意保留在 master 上:发布作业仅将其读取为发布说明正文,并不会删除它。下一个发布周期会覆盖该文件,因此无需手动清理。

对于正常的 workflow_dispatch 流程,Docker Publish 会在稳定版发布工作流中同步运行。如果所有发布作业均为绿色,则无需单独检查 Docker。如果维护者改为通过推送 vX.Y.Z 标签来启动发布,Docker Publish 会作为单独的标签触发运行启动;在将容器发布视为完成之前,请确认该同级运行状态为绿色。只有当 crates.io、Scoop 和 AUR 的作业显示为红色时,才需要单独关注它们。Homebrew Core 不属于此工作流;其 autobump 服务 会按照自己的计划检查符合条件的 formula。

想要验证已发布制品上的签名、SBOM 或 SLSA 来源信息的使用者,可以参阅发布制品验证。

在任何 release-attestation 工作流变更之后,人工维护者还必须在关闭跟踪问题之前,运行 docs/maintainers/release-attestation-runbook.md 中的联网及离线验证演练。本地工作流 lint 或 act 运行无法替代该发布级别检查,因为两者都无法生成 GitHub 的生产 OIDC 证明。


第 7 步:版本化文档部署

ZeroClaw 文档在 gh-pages 分支上使用版本化结构。Release Stable 工作流的 deploy-docs 作业会在 publish 成功后,针对发布标签分派 Deploy mdBook docs to Pages 工作流;该被分派的运行会异步构建该版本的文档并发布到 /vX.Y.Z/(分派作业不会等待它完成)。下面的引导和版本下限详细信息是在你需要重新创建 gh-pages 或更改受支持版本窗口时的参考资料。

为什么使用显式调度,而不是标签推送触发器。 docs-deploy.yml 列出了 tags: [v*],但发布标签由 publish 作业通过使用 GITHUB_TOKEN 的 gh release create 创建。GitHub 不会因使用 GITHUB_TOKEN 创建的标签推送而启动新的工作流运行(文档),因此以这种方式创建发布时,tags: [v*] 触发器永远不会触发。因此,deploy-docs 作业会通过 workflow_dispatch 调用 docs-deploy.yml(文档所述的即使在 GITHUB_TOKEN 下也会运行的例外情况),并将标签作为输入。如果你曾经改用个人令牌手动创建标签,那么 tags: [v*] 推送触发器会触发,而 release-workflow 调度只是同一部署的无操作重新运行,这两条路径最终都会汇聚到 /vX.Y.Z/。

自动执行的操作

  • deploy-docs 作业会触发一次构建,该构建最终位于 /vX.Y.Z/。
  • “稳定版”是指向该发布版本现有文档目录的指针。bump-version.sh 会写入 docs/book/stable-version.txt;只有 master 分支的运行才会将该指针发布到 gh-pages,并刷新根重定向和版本选择器。普通的 master 部署也会构建开发版文档和共享资源。不存在重复的 /stable/ 目录树。
  • **排序很重要:**版本号提升的 master 部署通常先于发布标签的部署。在 /vX.Y.Z/ 存在之前,它会保留之前的线上指针。发布流程中的 deploy-docs 任务只负责触发标签构建:该派发任务显示成功,并不能证明文档构建已经完成。待该运行成功后,请使用下方的晋升模式发布稳定指针,无需再次完整构建 master。
  • gh-pages 是临时分支:发布时会被替换为一个单独的孤立提交(orphan commit)。普通构建通过 DOCS_KEEP_VERSIONS 实施保留策略(保留 master、stable 指针指向的版本,以及最新的 N 个正式发布版本)。升级(promotion)时会保留所有已有的版本目录。
  • _shared/ 目录(包含 UI 的 CSS、JS 和图标)会在构建时更新,从而使主题级联应用到所有已部署的版本。
  • 已翻译的语言区域(es、fr、ja、zh-CN)从 docs/book/po 子模块渲染,该子模块会通过 submodules: recursive 按部署的 ref 所固定的提交来解析。这个固定是在版本提升时设置的;有关刷新、打标签和固定的流程,请参见 Step 2。英文不需要子模块。

部署已完成的版本发布

触发 Deploy mdBook docs to Pages 工作流,分支选择 master,将 mode 设为 promote-stable,并将 tag 设为确切的最终标签,例如 v0.8.5。默认的 build 模式保留常规构建行为。

晋升操作会更新 stable-version.txt、index.html、versions.json,以及站点根目录下的 llms.txt / llms-full.txt 文件对。该文件对镜像被晋升版本的英文内容;当该版本未同时包含这两个文件时,它们会被撤下,从而确保固定的 LLM 地址始终与 / 重定向到的版本保持一致。该操作不安装 Rust 或 mdBook 工具,并保留所有语言版本、API 参考、共享页面框架以及保留状态。工作流现有的 gh-pages 并发组会串行化两种模式;晋升操作还使用精确的推送租约,以避免替换已被其他写入者修改的站点。

该辅助脚本要求:派发提交必须仍是受保护的 master 当前 HEAD,其已提交的指针必须与请求的标签一致,并且 GitHub Latest 必须指向同一个公开的正式发布版本。脚本会将标签解析为提交,并将其与已部署版本的 .docs-source-commit 记录进行比对。该发布版本的 locales.toml 中列出的每个语言区域,都必须有非空的落地页。元数据缺失或不一致、API 错误、预发布版本、低于文档最低版本的版本,以及数字版本降级,都会在发布前导致失败。实时检查会在推送前立即重复执行。如果 master 在运行等待期间前进,则派发一次新的运行。重复执行已成功的推广是安全的。

部署于引入源码凭证之前的版本,需要先对该发布标签执行一次正常的 build 派发,之后晋升才能验证其源码。请勿手动创建凭证。晋升不会重写页面级的 canonical 与语言备用(alternate)标签,也不会更新 sitemap.xml;这些 SEO 字段将在下一次正常的文档部署时刷新。如需立即刷新 SEO,请使用正常的 master 构建。降级需要单独审核的手动回滚;该模式不提供回滚覆盖选项。撤销工作流变更会移除手动模式,但不会改变已发布的指针。

正在初始化 gh-pages

如果 gh-pages 曾被删除或需要完全重新创建,请按以下特定顺序初始化各版本:

  1. 支持的最早版本: 标签为 v0.7.5 的 workflow_dispatch
  2. 后续发布: 使用 workflow_dispatch 并指定标签 v0.8.0-beta-1 等。
  3. 当前 master:workflow_dispatch 使用标签 master

[!IMPORTANT] 在引导期间,master 必须最后部署。它会写入所有其他版本使用的最终 _shared/ chrome 层。

[!NOTE] Stable 版本从 docs/book/stable-version.txt 解析(已提交至源码,并发布到 gh-pages 根目录下的 stable-version.txt)。引导完成后,请确认该文件指向预期的 GA 版本;根目录重定向和“Stable (latest release)“选择器条目都会依据该文件。不会创建 /stable/ 目录。

手动重新部署与版本下限

手动重新部署特定版本:

  1. 前往 Actions → Deploy mdBook docs to Pages
  2. 单击 Run workflow
  3. 输入标签(例如 v0.7.5 或 master)

DOCS_MIN_VERSION 下限: 为防止意外部署非常旧或不受支持的版本,该工作流强制实施最低版本下限(当前为 v0.7.5)。

  • 工作流会拒绝早于 DOCS_MIN_VERSION(例如 v0.7.4)的标签。
  • cargo mdbook gen-versions(xtask 辅助工具)会忽略 gh-pages 上低于此下限的任何目录,将它们排除在版本下拉菜单之外。

如果你需要提高最低版本要求以放弃对旧版本的支持:

  1. 更新 .github/workflows/docs-deploy.yml 中的 DOCS_MIN_VERSION 环境变量。
  2. 下次部署时,旧版本目录会通过 DOCS_KEEP_VERSIONS 保留策略自动清理;无需手动编辑 gh-pages 即可回收空间。

如果出现问题

运行立即终止并显示 startup_failure(零个作业被创建): 将此视为症状,而非允许列表诊断。检查运行摘要和仓库 Actions 策略。如果 GitHub 报告了 selected-actions 拒绝,且发布工作流最近添加或更改了 uses: 引用,请将这些引用与允许的 actions 进行比对。仅在 Settings → Actions → General 中添加被拒绝的模式,等待几分钟以使设置生效,然后分发一次新的运行。如果 GitHub 未报告策略拒绝,请改为排查工作流定义或其他仓库策略。

validate 失败:版本不匹配: 版本升级 PR 未被合并,或者您输入了错误的版本。修复此不一致后重新触发。

crates.io 预检失败: 未发布任何内容。GitHub Release 会等待此任务完成,而该任务不持有任何注册表令牌。在手动触发的发布中,标签也尚未创建。请先在 master 上修复打包问题,然后针对同一版本重新手动触发 Release Stable。在本地运行 scripts/release/publish-crates.sh 以确认修复;不带 --execute 时,它只会打包并验证。

Plugin Smoke 某个 leg 失败: 在分发(dispatch)发布时,什么都不会发布:GitHub Release 以及发布工作流自身的镜像推送都会等待所有 leg 完成。由标签推送(tag push)触发的发布还会单独触发 Docker Publish,该运行不会等待 smoke 结束,因此也需要检查。打开该 leg 的 step summary:报告会指出失败的检查项并引用命令输出;plugin-smoke-<target> 产物中包含同样的报告。若失败出现在 plugin install 或工具结果步骤,说明为该目标构建的归档无法运行插件;请在 master 上修复,然后重新分发 Release Stable。先用 Cross-Platform Build 工作流演练修复,它会对所构建的二进制文件运行相同的 leg。若失败信息指向 provider 或临时目录(scratch directory),则是 runner 问题:仅重新运行失败的 leg。超时失败需重试一次;若再次超时则属于缺陷,因为加载插件时挂起的二进制文件同样会超时。

构建分支未通过二进制文件大小检查: 上限为 80 MiB,通过构建任务中 Check binary size 步骤上的 BINARY_SIZE_HARD_LIMIT 设置。在提高该上限之前,请使用 Cross-Platform Build 工作流测量二进制文件大小,并在提高上限的拉取请求中记录各目标平台的大小。

环境门控超时: 仅重新运行超时的作业。无需重启整个工作流。

Scoop 或 AUR 分发作业失败: 每个都有一个对应的可手动触发的子工作流。先使用 dry_run: true 重新运行相应的子工作流以确认修复有效,然后再使用 dry_run: false。这些属于锦上添花:分发作业失败不会使发布本身失效。对于 Scoop 凭据失败,请使用 Scoop Bucket Canary,而不要将通用试运行视为凭据证明;该金丝雀会启用故障关闭的 credential_canary 路径。

crates.io 发布程序在上传部分 crate 后中止: 不要提升版本号,也不要启动第二次发布。crates.io 上的版本无法替换或删除。请在同一个发布提交上修复失败的 crate,然后以 dry_run: false 对同一个标签重新运行 Pub crates.io;发布程序会先查询每个 <crate>@<version>,并跳过已经成功发布的版本。请阅读最后一个成功 crate 的 Publish 步骤。如果预检失败,则说明尚未尝试上传,问题仍可撤销。如果发布任务报告 web/dist 与预检验证的产物不一致,或者标签未解析到发布提交,则说明没有任何内容被上传;请再次触发 Pub crates.io,以便新的预检重新构建并重新验证。

发布失败出在发布脚本中,而非 crate 本身: v0.8.5 的两次失败都属于这一类。请勿移动标签,也不要手动发布。在 master 上修复脚本,然后从 master 为同一标签触发 Pub crates.io。crate 仍然基于标签打包,但 scripts/release/ 下的脚本来自 master。运行摘要中两个提交显示为 commit 和 release tooling。解析器只接受来自已包含发布提交的 master 提交的更新版工具,并且仅限手动触发。发布运行始终使用其标签所对应的脚本。

scoop 作业失败,并显示 remote: Permission ... denied to <account>(403): 这是权限问题,而不是清单问题:bucket 令牌已失效或权限范围不足。按照轮换 SCOOP_BUCKET_TOKEN中的说明轮换令牌,然后触发 Scoop Bucket Canary,在不写入 bucket 的情况下确认修复结果。使用 dry_run: false 重新运行 Scoop 发布器,并确认 bucket 已包含新版本。bucket 侧的 Excavator 恢复仍取决于 zeroclaw-labs/scoop-zeroclaw#1、仓库工作流写入权限和维护者冒烟测试;在这些步骤完成之前,不要等待它修复发布。

**每周的 Scoop Bucket Canary 失败了:**令牌已过期或失去了写入权限。使用相同的轮换流程。在下一次发布前修复它。

Homebrew Core 已过期: Homebrew 不是发布工作流作业。请查阅 Homebrew 自动版本更新状态及已记录的手动更新路径,而不是添加仓库 fork token。

**AUR 作业因 The AUR is down due to maintenance 失败:**这是上游服务中断,而非凭据问题。如果日志在 SSH key diagnostics 下显示了密钥指纹,且失败来自服务器而非 SSH,则 AUR_SSH_KEY 没有问题。发布器会在大约七分钟内重试五次,并设置严格的作业超时。每次尝试都会重新克隆当前软件包;如果其他运行已经发布了更新版本,则会停止,而不是将其降级。出现维护错误意味着维护窗口持续时间超过了重试额度。等待 aur.archlinux.org 恢复,然后在发布标签上重新分派 Pub AUR Package,先设置 dry_run: true,再设置 dry_run: false。使用 curl -fsS 'https://aur.archlinux.org/rpc/v5/info?arg%5B%5D=zeroclawlabs' 确认结果,或者直接分派 AUR Freshness Check。跳过此步骤会导致 AUR 静默落后,直到每周检查发现该问题。

AUR 比有意回滚的稳定版本更新: 验证回滚标签和软件包内容。如果已发布的软件包包含非零的 epoch,而回滚标签中没有该值,请勿重新调度旧标签:发布元数据取自不可变标签,因此默认分支上的编辑无法更改该次运行。相反,应准备一个包含已回滚代码且版本号递增的稳定版本,将匹配的 epoch= 赋值添加到 dist/aur/PKGBUILD,运行 cargo generate installers 重新生成 dist/aur/.SRCINFO,检查这两个文件,合并,然后创建新的发布标签。在发布该标签之前,新鲜度检查仍会显示为失败。绝不要使用 allow_downgrade 跨越 epoch 边界。对于同一 epoch 内的回滚,先运行一次手动 Pub AUR Package 工作流,并将 dry_run: true 用于验证元数据生成和版本保护检查的目标侧,然后再使用 dry_run: false 和 allow_downgrade: true 运行。非 dry-run 保护检查还会比较新克隆的 AUR。该覆盖选项仅存在于手动调度中;可复用接口未声明此输入,因此无法请求它。绝不要用它绕过格式错误的 AUR 元数据或无法解释的版本不匹配。

**由于同一版本的包文件存在差异,发布已停止:**发布器有意拒绝在现有的 epoch:pkgver-pkgrel 元组中替换不同的文件。请检查差异。获得授权的 AUR 维护者必须恢复由该发布标签生成的规范 PKGBUILD 和 .SRCINFO,或者合并经过修正的源代码变更,并在新的稳定发布标签下发布。编辑默认分支并重新触发旧标签无法奏效,因为发布器会从不可变标签中读取元数据。

**发布流程报告当前 AUR 版本为非数字或存在其他格式错误:**自动发布器会有意采取失败关闭策略,allow_downgrade 无法绕过格式错误的元数据。授权的 AUR 维护者必须通过手动推送 AUR,将软件包修复为格式正确的 epoch:pkgver-pkgrel,通过 AUR RPC 进行验证,然后重新触发常规发布器。不要弱化保护措施,以使格式错误的已发布状态可进行比较。


移除旧版工作流

以前位于 .github/workflows/ 中的几个自动发布工作流已被删除,因为它们绕过了审查或会进行不可逆的发布。它们已不再存在;如果其中任何一个在 PR 中重新出现,请将其视为回退并予以拦截:

工作流移除原因
release-beta-on-push.yml每次推送到 master 时自动发布
publish-crates-auto.yml在任何版本变更时自动发布到 crates.io,不可逆
version-sync.yml以机器人身份直接提交到 master,绕过审查
checks-on-pr.yml重复的 CI:产生了令人困惑的冲突状态
pre-release-validate.yml未使用的自动生成检查清单;此运维手册将取而代之

保留下来的工作流(自动和手动)的完整清单见 CI & Actions。


未来展望

本运行手册和 release-stable-manual.yml 只是一个过渡方案,而非最终目标。

目标最终状态:

  • release-plz 自动管理版本升级和变更日志
  • 单个 release.yml 取代了当前由多个子工作流拼凑的方案
  • SLSA 来源证明已内置于流水线中
  • 团队通过合并发布 PR 来发布版本,而不是按照操作手册执行

在该功能落地之前,请使用此流程。每次你按照本手册手动发布版本,都是为自动化所需功能积累经验的实践。