CI & Actions
每个 workflow 都位于 .github/workflows/。下面的部分按触发方式对它们分组:基于 git 事件自动触发,或通过 workflow_dispatch 和计划执行的由维护者调用/建议性 workflow。
自动工作流
质量门禁(ci.yml)
在每个针对 master 的 PR 以及向 master 的可信推送时触发。包含多个矩阵分支的组合任务:
- fmt:
cargo fmt --all -- --check - history-guard:获取完整历史记录,并针对
origin/master检查正在测试的提交;拉取请求使用显式的github.event.pull_request.head.sha,而受信任的推送和合并队列运行使用github.sha。该防护及其测试夹具会拒绝空的git merge-base,防止嫁接出的第二个根提交在合并后导致git blame崩溃 - 代码检查:
cargo clippy --workspace --exclude zeroclaw-desktop --all-targets --features ci-all -- -D warnings,然后执行cargo doc --no-deps --workspace --exclude zeroclaw-desktop(通过.cargo/config.toml中的build.rustdocflags将 rustdoc 警告视为致命错误;排除 desktop 以匹配xtask build_api/ docs-deploy,并避免 lint 运行器上的 GTK/glib-sys),以及注释规范门禁 - build: 矩阵:
x86_64-unknown-linux-gnu、aarch64-apple-darwin、x86_64-pc-windows-msvc - check:对工作区(不包括
zeroclaw-desktop)执行三轮将警告视为错误的检查:启用所有 features;不启用默认 features;以及在默认 features 下使用--all-targets,后者是唯一会在默认 feature 配置下编译测试目标的一轮 - check-32bit:
i686-unknown-linux-gnu,不启用任何默认特性 - bench:基准测试编译检查
- 测试:在 Linux 上运行来自
scripts/ci/firmware_protocol_gate.sh的独立固件协议主机端门禁和cargo nextest run --locked --workspace --exclude zeroclaw-desktop,包括配置写入隔离和 Fluent 覆盖率(不得存在裸露的面向用户字符串)架构检查 - parallel-runtime-test:来自
scripts/ci/parallel_runtime_test_gate.sh的重复同进程运行时/通道测试,与主测试任务并行运行,适用于相关 PR 路径,并在master推送和合并队列运行中无条件执行 - security:
cargo deny check - nix-eval:评估 NixOS 模块断言(
nixos-module-evalflake 检查) - docs-style: markdown lint、em-dash 书面语检查,以及通过
scripts/ci/docs_quality_gate.sh和scripts/ci/docs_links_gate.sh进行变更行链接门禁
fmt 首先作为低成本的串行门禁运行。其他所有作业都会直接或传递地声明 needs: [fmt],并在格式化通过后扇出执行;CI Required Gate 会聚合所有结果。分支保护会固定该复合门禁作业。PR 在此项变为绿色之前无法合并。master 的 push 运行会保持相同的质量信号,同时为后续 PR 运行预热受信任的 Rust 缓存。
Fresh 所需的 CI 通常是 Cargo 实际运行的那些 surface 所共享的证据。对同一 head、target 和 feature set 重新在本地运行相同的 Cargo 命令,只是重复确认,而不是更强的证明。在请求额外的 Cargo 或 Clippy 之前,请将变更的 surface 与当前工作流文件以及 PR 上实际执行的检查进行比较。额外验证应放在所需 gate 不能证明审查对象的地方:
- 某个平台收到了编译检查,但没有收到测试;
- 平台、crate 或路径位于所需的 lint 作业之外;
- 桌面更改没有触发桌面工作流;
- 发布目标位于 PR 矩阵之外,仅由 release/manual 工作流覆盖;
- 过期、已取消、已跳过或不可用的 CI 不是新的证据。
当定义或导入受 feature 条件控制时,请将其 cfg 谓词与每个使用方进行比较。验证启用配置和每个相关的禁用配置:启用 feature 的检查可以证明使用方仍能正常工作,而工作区范围的 no-default-features 检查则能捕获会产生警告的不匹配,例如未使用的私有定义或导入。该检查会在不带 --all-targets 的情况下运行 cargo check,因此从不会编译测试目标:一个受普通 test 条件控制、且其唯一调用方都位于某个 feature 后面的辅助函数,反而会由 default-features/all-targets 检查流程捕获。当所需的两个 CI 配置都没有覆盖已更改的谓词时,仍然需要针对性的 feature 组合。
定期平台测试 (platform-tests.yml)
在一次低成本的 Linux 格式检查之后,在 macos-14 和 windows-latest 上运行 cargo nextest run --locked --workspace --exclude zeroclaw-desktop --no-fail-fast。矩阵针对以下内容运行:
- 修改
platform-tests.yml本身的拉取请求; - 手动调度;以及
- 每晚 03:17 UTC 的计划。
这些作业使用 continue-on-error,不会为 CI Required Gate 提供输入。它们是可移植性证据,而不是合并要求。普通代码 PR 不会自动启动该矩阵;在需要针对特定平台进行验证时,维护者可以针对某个分支手动调度它。该工作流不会因普通的 push 或 merge_group 事件运行。在 master 上的夜间运行和手动调度运行可以写入受信任的缓存;拉取请求运行则不能。--no-fail-fast 会让单次运行中的每个平台失败都可见。
每日顾问扫描 (daily-audit.yml)
每日 UTC 时间 09:00 针对依赖树运行 cargo deny check advisories。发现问题时创建 issue。除非报告漏洞,否则不执行任何操作。
每日 npm 审计 (daily-npm-audit.yml)
每天在 09:23 UTC 对 web/package-lock.json 运行 npm audit --audit-level=high。当高严重性 npm 安全公告影响已提交的 web 锁定文件时,打开一个去重的 security + dependencies 问题。
每周 Trivy 镜像扫描 (trivy-scheduled.yml)
每周六扫描已发布的 dist 和 default-features GHCR 镜像,并将 HIGH/CRITICAL 发现项以 SARIF 形式上传到 Security 选项卡。扫描以报告优先(针对发现项设置 exit-code: 0),但如果预期镜像缺失,则会在 Trivy 设置之前使作业失败,并在错误中指出缺失的标签以及所属的发布者工作流。
每周 Scoop Bucket Canary (scoop-bucket-canary.yml)
每周一针对当前稳定版本演练 Scoop 发布路径。它会解析最新的 vX.Y.Z 标签,并使用 dry_run: true 和 credential_canary: true 调用 pub-scoop.yml,因此会使用真实的 SCOOP_BUCKET_TOKEN 针对真实存储桶执行测试,而不会写入任何内容。
credential_canary 是该契约中“失败关闭”的部分:缺少 SCOOP_BUCKET_REPO 或 SCOOP_BUCKET_TOKEN 会导致运行失败,并且配置的凭据必须传递到 git push --dry-run 授权探测。仅使用 dry_run: true 的通用手动 pub-scoop.yml 运行在清单生成方面仍然是宽松的,并且在凭据不可用时可能会跳过该探测;不要将通用模式用作凭据验证的依据。
之所以存在这一机制,是因为 SCOOP_BUCKET_TOKEN 与账户绑定:它会过期,并且当所属身份在存储桶上的协作者授权发生变化时,会悄无声息地失去写入权限。这两种情况都发生过。在 canary 之前,唯一会实际使用该凭据的是发布后的 scoop 作业,因此直到版本已经定稿并发布公告之后,才发现令牌已失效,而存储桶只能手动更新。
金丝雀用于检测凭据失效。它有意不负责确保存储桶状态正确,也未接入 Release Stable:失效的包管理器凭据绝不能阻塞或延迟发布。
Scoop bucket 如何保持正确性
目前,发布器是唯一的自动化写入者:
pub-scoop.yml在发布时推送。 Scoop 用户在此操作成功后会立即看到新版本。它需要跨仓库的SCOOP_BUCKET_TOKEN,这是最容易出问题的部分。- 维护者恢复失败的推送。 轮换或修复令牌,调度 Scoop Bucket Canary 通过故障关闭的
credential_canary路径对其进行验证,使用dry_run: false重新运行发布器,并确认 bucket 清单已写入发布版本。
scoop-zeroclaw#1 中提出了由 bucket 侧 Excavator 执行的方案。该工作流合并后,bucket 仓库授予 Actions 读写工作流的权限,并且维护者的冒烟测试证明它能够提交更新后,它就可以成为不依赖凭据的恢复层。在满足这三个条件之前,不要假定失败的发布器会自动自愈。
checkver 和 autoupdate 块对于计划中的 Excavator 路径已经不可或缺。当前的推送路径还使用 scripts/release/scoop_metadata.sh 从 autoupdate 推导其发布 URL 模板,因此两条路径共用同一个清单契约。不要删除这些块,也不要手动将其从 dist/scoop/zeroclaw.json 中移除。
PR 路径标签器 (pr-path-labeler.yml)
根据更改的文件自动应用路径和范围标签。它会在 PR 打开、重新打开以及每次向 PR 分支推送更新时运行。由于启用了 sync-labels: true,.github/labeler.yml 中定义的标签会根据当前 PR 文件集重新计算。
此工作流当前不会应用 risk:*、size:*、type:*、贡献者等级、状态、解决方案、stale 或 pickup 标签。如果某个 PR 缺少路径/范围标签,请检查 .github/labeler.yml 中的路径是否覆盖了相关更改。
Dependabot 在 .github/dependabot.yml 中为其自身的 PR 提供了单独的标签配置。Cargo 更新 PR 以 dependencies 开头;GitHub Actions 和 Docker 更新 PR 以 ci 和 dependencies 开头。
项目仪表板规划器(project-dashboard-plan.yml)
手动运行以处理单个 issue 编号。它会读取 issue 状态和标签,然后写入一条仅供报告的步骤摘要,建议最符合该 issue 的现有 Project Status 值。
此工作流不会在 issue 事件、写入 ProjectV2 字段、编辑 issue、添加标签、发布评论,或重新计算 PR risk:*、size:* 或 type:* 标签时自动运行。实时 ProjectV2 变更或自动 issue 事件规划需要单独批准的字段映射、触发策略以及项目范围凭据。
验证 PR 标题(pr-title.yml)
在每次 PR 打开/编辑/同步时运行。运行验证器单元测试(scripts/check-pr-title.test.sh),并依据 Conventional Commits 检查 PR 标题(scripts/check-pr-title.sh)。
将 mdBook 文档部署到 Pages(docs-deploy.yml)
在标签推送时触发(以及 workflow_dispatch);构建并将带版本的文档发布到 gh-pages 分支。有关版本下限和引导规则,请参阅 发布操作手册 → 版本化文档部署。
Docker 镜像 PR 检查(docker-image-pr.yml)
仅在 Docker 镜像、Compose 或 release-Docker 上下文文件发生更改时运行。它会验证合并后的 default-plus-Alpine Compose 配置;如果更改不只是 Compose 编辑,还会构建默认和 Debian 预构建冒烟镜像以及源 Dockerfile 对应的镜像,但不会推送这些镜像。默认和 Alpine 源镜像会为 linux/amd64 和 linux/arm64 构建;Debian 源镜像会为 linux/amd64 构建。独立的 Alpine 和 Debian linux/amd64 流水线会启用 plugins-wasm-runtime-only,使其构建器上下文持续证明仓库 WIT 契约可用于启用插件的源代码构建。
当该文件或 Docker 工作流发生更改时,全功能 Containerfile 源镜像会针对 linux/amd64 构建。它使用隔离的缓存范围,既不会加载,也不会推送。Alpine amd64 任务会运行两个二进制文件,通过合并后的 Compose 配置启动构建出的镜像,并检查网关健康状态和仪表板界面。Alpine arm64 任务仅覆盖编译和镜像组装。仅涉及 Compose 的更改使用精简的 Alpine amd64 矩阵,因此仍会验证运行时契约,而无需重新构建无关镜像。所有作业都仅具有仓库只读权限,且没有注册表写入权限。
Docker 发布(docker-publish.yml)
构建、签名并扫描从 dev/ci/docker-tags.toml 生成的四变体矩阵:minimal、default-features、dist 和 all-features。人工创建的 v* 标签会直接启动此工作流。通过 workflow_dispatch 启动的稳定版发布会使用 GITHUB_TOKEN 创建其标签,而这不会触发另一个标签推送事件,因此在规范发布和 Docker 作业成功后,release-stable-manual.yml 会在不可变的发布标签处同步调用 Docker Publish。
此矩阵是对稳定版本预构建的 latest、版本化及 debian 镜像的补充,而非替代。两条路径使用不同的构建输入并发布不同的标签。
Discord 发布(discord-release.yml)
在稳定版发布成功后触发。将发布说明发布到社区 Discord。
推文发布 (tweet-release.yml)
在稳定版发布成功后触发。发布一条公告推文。
每周 AUR 新鲜度检查 (aur-freshness-check.yml)
每周一将已发布的 zeroclawlabs AUR 版本与当前稳定版 GitHub 发布版本进行比较,如果 AUR 版本落后则失败。
发布到 AUR 是一种即发即忘的操作:如果 pub-aur.yml 失败,就不会有任何机制重新检查,因此软件包会悄无声息地落后。v0.8.4 之后发生的正是这种情况。aur.archlinux.org 的维护窗口与发布重叠,唯一一次未重试的克隆操作因 The AUR is down due to maintenance 而失败,软件包在没有任何信号的情况下落后三周。现在,发布器最多允许一个活动中的非试运行发布,并会进行重试以应对短暂中断;在同一并发组中,GitHub 可能会取代较早排队的真实发布,而试运行使用单独的组。每次尝试都会重新克隆权威的软件包状态,并拒绝用较旧的 epoch:pkgver-pkgrel 元组替换较新的元组。重试预算仍无法覆盖所有失败,因此此检查就是后备保障,可将无声遗漏或被取代的运行转化为可见的问题。
如果无法访问 AUR RPC,检查会发出警告并通过,而不是失败。AUR 中断属于上游可用性问题,而不是软件包过时问题,下次计划运行时会重新检查。过时状态是持久的,因此延迟检测可以接受;但每周因他人的维护窗口而触发告警则不可接受。
文档在发布流水线中构建和发布,而非每次推送 master 时构建。翻译是一个仅限本地的工作流,适用于专用翻译缓存 PR、新语言区域及发布翻译流程。常规英文文档 PR 可以推迟大范围生成的 .po 变更。贡献者指南请参阅 Docs & Translations,发布流程请参阅 Release Runbook。
手动和建议性工作流
每月过期扫描(monthly-outdated.yml)
每月 1 日 09:00 UTC 定时扫描。对所有工作区成员运行 cargo outdated --workspace。当发现过期依赖时,创建一个带有 dependencies-标签的 issue。权限:contents: read + issues: write。去重保护可在前一个 issue 仍然打开时防止重复累积。
新问题的第一步分诊:检查所报告的过期 crates 是否存在与 semver 不兼容的版本跳变,以及下游 crate 的 API 是否发生了变化。如果版本跳变是琐碎的(patch/minor),则创建一个简短的仅依赖项 PR。如果升级因 semver 破坏而受阻,则关闭该问题,并附上一条说明和阻塞的 crate 名称。
跨平台构建(cross-platform-build-manual.yml)
用于手动触发构建覆盖完整目标矩阵的发布二进制文件:Linux x86_64/aarch64 GNU 和 MUSL,以及 armv7 和 arm 硬浮点,macOS Intel/ARM、Windows x86_64,以及 aarch64-linux-android(使用 NDK 构建)。在打标签之前,使用此功能验证分支能否在非 Linux 目标上顺利编译。
每次调度还会独立于构建运行一个小型发布工具冒烟测试矩阵。当只需要此项验证时,设置 release_tools_only;随后会跳过 web 和 release-build 作业。在受信任的 GitHub 托管 Linux x86_64 上,冒烟测试安装固定版本的 cross 归档,确认 cross 和 cross-util 均可用,并记录 cross --version。在受信任的 GitHub 托管 Windows x86_64 上,它使用与稳定版发布工作流相同的 Rust 版本和 Bash 到 Cargo 的路径形式,然后记录 cargo-tauri.exe --version 和 cargo tauri --version。每个执行分支都会在公开作业摘要中记录确切的测试提交和运行器架构。冒烟测试使用只读仓库权限,并且没有发布作业、环境、机密或制品上传。
MUSL 构建任务也会通过 scripts/ci/install_release_tool.sh 安装 cross;该脚本会下载精确锁定的上游发布资源,并在安装前验证其 SHA-256。必需的 Repository Structure 作业会测试受支持的 runner 到资源的映射以及冒烟工作流契约,且不会发起网络调用。
跨平台 Clippy (cross-platform-clippy.yml)
在 macOS aarch64 和 Windows x86_64 目标上的手动及每周定时建议性 lint 覆盖。它镜像了所需的 PR lint 命令,并为每个平台设置了 --target,但故意不在 PR 上运行,也不属于 CI Required Gate。
必需的 Linux Clippy、建议性的跨平台 Clippy 以及针对性的 Windows Clippy 都调用 scripts/ci/run_clippy.sh。该运行器负责支持的命令形式、Cargo 退出状态的传递,以及共享的耗时、缓存、编译次数和下载次数诊断信息。工作流文件继续负责触发器、运行器、工具链、缓存、超时设置以及必需门禁的纳入。
发布稳定版(release-stable-manual.yml)
手动触发完整发布流水线。构建所有目标,创建 GitHub Release,将预构建的 latest、带版本号的以及 debian Docker 镜像推送到 GHCR,针对发布标签调用生成的 Docker 变体矩阵,触发网站重新部署,并调用分发子工作流(Scoop、AUR、Discord、tweet)。Homebrew Core 通过其自身的 autobump 服务检测新发布版本。两个环境关卡需要维护者在运行过程中批准:github-releases(publish 作业)和 docker。
可下载的资源使用 GitHub 托管的 Build Level 2 证明。离线捆绑包和受信任根材料随附在一个验证归档文件中,两种 SBOM 格式在创建发行版之前都会进行校验和计算和证明。Cosign 仅限用于 GHCR 镜像签名。
完整流程请参阅 Release Runbook。
仅用于发布的构建工具不会在每次运行时都从源代码编译。该工作流通过 scripts/ci/install_release_tool.sh 安装固定版本的上游 cross 和 Tauri CLI 发布二进制文件;该脚本会在将二进制文件放入 Cargo 的 bin 目录之前,验证每个运行器专用归档文件对应的仓库 SHA-256。更新任一工具时,都必须同时更新其版本、资源名称和校验和,然后运行 scripts/ci/install_release_tool.test.sh。
软件包发布者
每个工作流都在 workflow_dispatch 触发时运行,并接收一个版本输入参数。此外,它们也会在发布工作流成功发布后被调用。
| 工作流 | 它的作用 |
|---|---|
pub-aur.yml | 更新 Arch 用户仓库 PKGBUILD 并推送到 AUR |
pub-crates.yml | 打包并验证协调的工作区发布版本,然后在 crates-io 环境门禁下按依赖顺序将其发布到 crates.io |
pub-scoop.yml | 更新 Windows 的 Scoop 清单 |
Homebrew Core 的官方 autobump 服务会发现稳定的 GitHub 发布版本,并独立发起 formula 版本更新。不要恢复项目自有的 Homebrew 发布器或 fork token;这会重复 Homebrew 的权威自动化。
必需的密钥
| 秘密 | 用于 |
|---|---|
AUR_SSH_KEY | pub-aur.yml |
CARGO_REGISTRY_TOKEN | 仓库机密被显式传递给 pub-crates.yml,且仅由其受保护的发布作业引用;v0.8.5 需要使用 publish-new 发布 zerorelay、zeroclaw-relay-proto 和 zeroclaw-tls,而后续的协调更新需要使用 publish-update |
DISCORD_WEBHOOK_URL | discord-release.yml |
TWITTER_ACCESS_TOKEN、TWITTER_ACCESS_TOKEN_SECRET、TWITTER_CONSUMER_API_KEY、TWITTER_CONSUMER_API_SECRET_KEY | tweet-release.yml |
SCOOP_BUCKET_TOKEN | pub-scoop.yml、release-stable-manual.yml、scoop-bucket-canary.yml;仅限于 zeroclaw-labs/scoop-zeroclaw 且具有 Contents 读写权限的细粒度 PAT |
WEBSITE_REPO_PAT | release-stable-manual.yml(触发网站仓库重新部署) |
GITHUB_TOKEN(自动) | 所有推送提交、创建 PR 或将镜像推送到 GHCR 的工作流 |
Docker 镜像使用自动生成的 GITHUB_TOKEN 推送到 GHCR;不存在单独的注册表令牌。将 CARGO_REGISTRY_TOKEN 存储为仓库机密,并仅将这个指定的机密映射到可复用的发布器中。被调用的工作流仅在不可逆的发布步骤中引用它;该步骤所属的作业需要通过 crates-io 环境审批;无令牌的预检既不引用也不导出它。审批者启动发布作业之前,预检会对同一个不可变的发布提交进行打包。
协调发布集中的大多数 crate 已经存在,并且可以使用 crates.io 的可信发布功能。v0.8.5 版本还会创建 zerorelay、zeroclaw-relay-proto 和 zeroclaw-tls,因此其引导令牌必须包含 publish-new。在每个 crate 都为此工作流配置可信发布者条目之前,环境令牌仍是引导路径。配置这些条目后,迁移该作业,使 GitHub 将 OIDC 身份交换为短期令牌,而不是继续保留 CARGO_REGISTRY_TOKEN。
该组织当前在 Scoop bucket 上禁用了部署密钥,而自动生成的 GITHUB_TOKEN 无法写入另一个仓库。请将 SCOOP_BUCKET_TOKEN 的权限范围严格限制在该 bucket 内;不要重复使用维护者的宽泛 CLI 令牌。发布器会通过 git push --dry-run 检查写入权限,然后使用相同的 Git 传输通道执行实际更新。
轮换 SCOOP_BUCKET_TOKEN
由于部署密钥不可用,此凭据是个人访问令牌,因此存在两种相互独立的失效模式,而这两种模式都曾导致一次发布出现问题:
- 令牌会过期。 细粒度 PAT 的有效期有上限,因此无论其他内容是否发生变化,此问题都会按固定周期再次出现。
- 所有者身份失去了对存储桶的写入权限。 即使令牌仍然有效,其背后的账户也可能只是一个
read协作者。这会产生remote: Permission to zeroclaw-labs/scoop-zeroclaw.git denied to <account>和 HTTP 403,而不是身份验证错误,因此看起来像是代码问题,实际却是权限问题。
使用 ZeroClaw-Bot 账户持有令牌,绝不要使用个人账户,这样发布流程就不会依赖某一位维护者的凭据。要轮换令牌:
- 以
ZeroClaw-Bot身份创建一个细粒度 PAT,资源所有者 为zeroclaw-labs,仓库访问权限 仅限于单个仓库zeroclaw-labs/scoop-zeroclaw,并设置 仓库权限 → 内容:读取和写入。除此之外不设置任何权限。 - 确认组织已批准该令牌。针对组织资源所有者的细粒度 PAT 在获得批准前会一直处于待处理状态,待处理令牌可以进行身份验证,但无法推送。
- 确认
ZeroClaw-Bot仍对该存储桶具有write权限:gh api repos/zeroclaw-labs/scoop-zeroclaw/collaborators/ZeroClaw-Bot/permission --jq '.role_name'。第 1 步不会授予仓库访问权限;它只会限定令牌可以使用的权限范围。令牌的权限不能超过其所有者已有的权限。 - 设置机密:
gh secret set SCOOP_BUCKET_TOKEN --repo zeroclaw-labs/zeroclaw。 - 无需接触存储桶,通过触发 Scoop Bucket Canary 进行验证。运行成功表明新令牌具备推送权限。
轮换时,将过期日期记录在某个持久的位置。无论如何,canary 会在一周内发现过期的令牌,但那时它已经失效了。
AUR 软件包所有权
项目所拥有的软件包目前是 zeroclawlabs,由 zeroclaw-bot 维护。规范名称为 zeroclaw 的软件包是第三方软件包,无法通过轮换 AUR_SSH_KEY 来接管。如果该维护者仍处于不活跃状态,请先遵循 AUR 孤儿请求流程,再更改 pkgname 或工作流的克隆目标。所有权转移后,请在一项经过审查的变更中协调软件包重命名或合并。
构建缓存行为
ci.yml 中大多数 Rust 工作负载较重的作业都通过本地 ./.github/actions/rust-cache 复合操作进行缓存,该操作根据用于选择运行器的同一个 CI_USE_BLACKSMITH 开关来选择缓存后端:作业在 Blacksmith 运行器上运行时使用 useblacksmith/rust-cache(Blacksmith NVMe 持久磁盘),否则使用 Swatinem/rust-cache。任何非 true 的开关值(包括未设置,以及所有来自 fork 的 PR)都会在 GitHub 托管的运行器上回退到 Swatinem/rust-cache,因此停用 Blacksmith 时也不会丢失缓存。无论开关值如何,这两个操作引用都存在于该复合操作中,因此都必须保留在允许列表中。macOS 和 Windows 构建任务继续使用 Swatinem/rust-cache,而 fmt、nix-eval 和 docs-style 作业(它们都不会编译工作区)不使用 Rust 缓存。在排查与缓存相关的偶发失败时,了解这些行为很有帮助:
- 缓存写入仅限 master 分支。
save-if的条件为github.ref == 'refs/heads/master',因此 PR 运行会读取由 master 填充的缓存,但绝不会更新它。PR 分支无法用特定于分支的产物污染共享缓存。master上的push触发器使得该工作流在合并后拥有一次可信的缓存写入运行。 - 失败时保存缓存。 每个作业都设置了
cache-on-failure: true,因此部分运行仍可为下一次尝试预热缓存。 - 已启用 Windows 构建缓存。 Windows 构建环节运行与 Linux 和 macOS 相同的固定版本 Rust 缓存 action。如果 Windows 缓存行为不稳定或出现回归,请还原此工作流更改,并在缓存 issue 中记录失败的 restore/save 证据。
- 增量编译已禁用。 工作流级别设置了
CARGO_INCREMENTAL: 0。增量构建会增大缓存体积,并在部分陈旧条件下产生不可复现的构建产物。 cargo-deny和cargo-nextest每次运行时都会全新安装。security作业会运行cargo install cargo-deny --locked;Linuxtest作业和定时运行的platform-tests.yml两个任务都会从get.nexte.st获取相应的cargo-nextest二进制文件。这两个工具都不会被缓存,因此每次安装都会给其所在的作业增加固定开销。如果改用taiki-e/install-action,就可以对它们进行缓存,但该操作目前不在允许列表中。
当闸门变红时
| 症状 | 首先要检查的事项 |
|---|---|
Release Stable(发布稳定版)在 uses: 引用变更后于 startup_failure 处中止,且没有任何作业 | 检查运行摘要和仓库的 Actions 策略。如果 GitHub 报告选择的操作遭到拒绝,请将变更后的 ref 与允许列表进行比较,仅添加被拒绝的模式,等待设置生效,然后重新触发一次运行。否则,请调查工作流定义或其他仓库策略;仅凭 startup_failure 无法确定原因 |
CI Required Gate 红色 | 先 fmt,然后 lint,接着 test,最后 build |
发布 validate 失败 | Cargo.toml 版本与工作流输入不匹配,或者标签已存在 |
| 发布构建阶段失败 | 特定目标的作业日志。Android 为 experimental,并启用 continue-on-error |
| 环境门限超时 | 从工作流运行页面重新运行超时的作业 |
| 分发发布者失败 | 首先,使用 dry_run: true 手动重新运行相应的子工作流 |
允许的操作
该仓库以 selected 模式运行 Actions,只有此允许列表中的操作才可运行。允许列表必须保持精简;新增的第三方操作在添加前需要维护者明确批准。
所有第三方引用都固定到完整的提交 SHA,并附带尾部版本注释;下面的版本列记录的就是该注释。
| 操作 | 用于 | 目的 |
|---|---|---|
actions/checkout (v6.0.2) | 大多数工作流 | 检出仓库 |
actions/cache (v4.2.3, v5.0.5) | docker-image-pr.yml, tweet-release.yml | 通用依赖和 Trivy 数据库缓存 |
actions/setup-node (v7.0.0) | ci-sbom.yml, ci.yml, cross-platform-build-manual.yml, daily-npm-audit.yml, pub-crates.yml, release-stable-manual.yml | 用于 npm SBOM 生成、Web 测试/审计以及 Web/桌面构建的 Node 工具链 |
actions/upload-artifact(v7.0.1) | release-stable-manual.yml, cross-platform-build-manual.yml, docker-publish.yml, trivy-scheduled.yml | 上传构建产物和 Trivy SARIF 交接产物 |
actions/download-artifact(v8.0.1) | release-stable-manual.yml、cross-platform-build-manual.yml、docker-publish.yml | 下载构建产物和 Trivy SARIF 交接产物 |
actions/attest (v4.2.2) | release-stable-manual.yml | 为发布资产生成 GitHub 托管的构建级别 2 来源证明 |
actions/labeler (v6.1.0) | pr-path-labeler.yml | 从 .github/labeler.yml 应用路径/作用域标签 |
dtolnay/rust-toolchain (stable, v1) | ci.yml, platform-tests.yml, pub-crates.yml, release-stable-manual.yml, cross-platform-build-manual.yml, cross-platform-clippy.yml, daily-audit.yml, docs-deploy.yml, codeql.yml | 安装 Rust 工具链 |
Swatinem/rust-cache (v2.9.2) | ci.yml(./.github/actions/rust-cache 的 GitHub 托管路径)、platform-tests.yml、pub-crates.yml、release-stable-manual.yml、cross-platform-build-manual.yml、cross-platform-clippy.yml、docs-deploy.yml | GitHub 托管运行器上的 Cargo 构建/依赖缓存 |
useblacksmith/rust-cache (v3.0.1) | ci.yml(Blacksmith 的路径 ./.github/actions/rust-cache) | Blacksmith 粘性磁盘上的 Cargo 构建/依赖缓存;仅在 CI_USE_BLACKSMITH=true 时选择 |
docker/setup-buildx-action (v3.11.1, v4.0.0) | release-stable-manual.yml, docker-publish.yml | Docker Buildx 设置 |
docker/login-action(v3.4.0、v4.1.0) | release-stable-manual.yml、docker-publish.yml、trivy-scheduled.yml | GHCR 身份验证 |
docker/build-push-action (v6.18.0, v7.1.0) | release-stable-manual.yml, docker-publish.yml | 多平台镜像构建与推送 |
sigstore/cosign-installer (v3.8.1) | release-stable-manual.yml, docker-publish.yml | 安装 cosign 以实现无密钥 GHCR 容器镜像签名 |
anchore/sbom-action (v0.24.0) | release-stable-manual.yml | 为每个发布生成 SPDX + CycloneDX SBOM。 |
aquasecurity/trivy-action (v0.36.0) | docker-image-pr.yml、docker-publish.yml、trivy-scheduled.yml | 仅报告模式的容器漏洞扫描 |
github/codeql-action/upload-sarif (v3.36.2) | docker-publish.yml, trivy-scheduled.yml, ci-code-analysis.yml | 将 Trivy 和 Semgrep SARIF 报告上传到安全选项卡 |
github/codeql-action/init (v3.36.2) | codeql.yml | 初始化 CodeQL 分析(Rust 和 JS/TS) |
github/codeql-action/analyze (v3.36.2) | codeql.yml | 上传 CodeQL SARIF 到安全选项卡 |
GitHub Release 本身是在 publish 作业内通过 gh release create 创建的,而不是通过 release action。
等效的白名单模式(故意保持狭窄):
actions/*
dtolnay/rust-toolchain@*
Swatinem/rust-cache@*
useblacksmith/rust-cache@*
docker/*
sigstore/cosign-installer@*
anchore/sbom-action@*
aquasecurity/trivy-action@*
github/codeql-action/upload-sarif@*
github/codeql-action/init@*
github/codeql-action/analyze@*
导出当前生效的策略:
sh
gh api repos/zeroclaw-labs/zeroclaw/actions/permissions
gh api repos/zeroclaw-labs/zeroclaw/actions/permissions/selected-actions
任何添加或更改 uses: 操作来源的 PR 都必须在正文中包含一个白名单影响说明。避免使用宽泛的通配符例外;仅针对已验证缺失的操作扩展白名单。
维护规则
- 保持
CI Required Gate的确定性和简洁性。向该门禁添加作业需要有明确的质量依据。 - 所有第三方操作引用必须固定到完整的提交 SHA(根据上述白名单策略)。
- 保持
ci.yml、dev/ci.sh和.githooks/pre-push一致。共享的检查项必须放在scripts/ci/中;每个调用方应调用该辅助脚本,而不是复制其命令。对于独立的固件协议检查项,文档中记录的本地入口点是./dev/ci.sh firmware-protocol。 - 保持
scripts/ci/prepare_docker_context.sh、docker-image-pr.yml以及release-stable-manual.yml中的 Docker 作业相互一致,以便 PR 验证所使用的上下文结构与发布工作流所发布的保持相同。 - 在更改发布证明、校验和、SBOM 或验证存档序列后,运行
python3 scripts/ci/release_attestation_contract_test.py。 docs-stylegate 作业运行bash scripts/ci/docs_quality_gate.sh(markdown lint + em-dash 文案检查)和bash scripts/ci/docs_links_gate.sh(变更行链接检查)。在推送 docs 变更之前,请先在本地运行这两个脚本。
紧急回滚
如果白名单在事件处理过程中锁定了关键操作:
- 临时将 Actions 策略恢复为
all。 - 在识别出缺失的条目后,恢复
selected的白名单。 - 记录事件和最终的白名单差异。
这是通往 all 模式的唯一合理路径,并且它的存续时间绝不应超过该事件本身。