Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
334 lines
18 KiB
Markdown
334 lines
18 KiB
Markdown
<!--
|
||
Licensed to the Apache Software Foundation (ASF) under one
|
||
or more contributor license agreements. See the NOTICE file
|
||
distributed with this work for additional information
|
||
regarding copyright ownership. The ASF licenses this file
|
||
to you under the Apache License, Version 2.0 (the
|
||
"License"); you may not use this file except in compliance
|
||
with the License. You may obtain a copy of the License at
|
||
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
Unless required by applicable law or agreed to in writing,
|
||
software distributed under the License is distributed on an
|
||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
KIND, either express or implied. See the License for the
|
||
specific language governing permissions and limitations
|
||
under the License.
|
||
-->
|
||
|
||
# Maka CLI npm 发布操作手册
|
||
|
||
[English](./cli-npm-release.md)
|
||
|
||
本文档是发布 `maka-agent` npm 安装渠道的操作权威。根目录 `package.json` 仍是 Maka 唯一产品版本权威,`packages/cli/package.json` 必须与其一致。每个公开 npm 版本都必须来自共享 package workflow 验证过的精确 tarball。
|
||
|
||
ASF 分发基础设施上经 IPMC 批准的源码归档才是 Apache release。npm、Desktop 安装包和
|
||
GitHub Release assets 都是从该源码身份构建的便利包,不是额外的 ASF release artifacts。
|
||
|
||
source RC 阶段的 [npm 预检](../.github/ASF_NPM_RELEASE.md) 是更早执行、且不持有发布凭据的兼容性检查;其 tarball 不会进入正式发布。source release 获批后,Stage 从位于同一获批 commit 的最终产品 tag 重新构建,并成为 npm staging 与 registry 验证所使用的字节权威。这与 Apache OpenDAL 孵化期的实践一致,同时保留了 Maka 更严格的受保护 Environment、staged publishing、2FA 与 Finalize 控制。
|
||
|
||
## 发布不变量
|
||
|
||
- 产品发布工作流只能从已批准的 ASF source candidate tag dispatch;npm Stage 与 npm
|
||
Finalize 都只能从 `main` dispatch,Stage 仅把随后创建的产品 `v<version>` tag 作为已验证数据;
|
||
- 获批的稳定版本使用 `latest`,开发快照使用 `nightly`;不再存在 `next` 渠道,`nightly`
|
||
永远不得修改 `latest`;
|
||
- 不创建 npm 专属 Git tag 或 GitHub Release;`Release` workflow 在 npm staging 前创建产品
|
||
`v<version>` tag 与 Draft,Finalize 是该 Draft 唯一的发布者;
|
||
- 在 npm Finalize 与 Desktop 远程 Runtime Host 验收成功前,GitHub Release 必须保持 Draft;
|
||
Draft 为 npm 提供产品身份,发布 Draft 是最终的产品发布动作;
|
||
- 正式发布只能运行 `npm stage publish`,由人工 package maintainer 使用 npm 2FA 批准;只有
|
||
Product Nightly 可以直接运行 `npm publish --tag nightly`;
|
||
- validation、staging、approval 和 finalization 之间不得重新构建;
|
||
- 已公开的版本不得复用。正式产品修复必须使用新的 patch、minor 或 major 版本。
|
||
|
||
workflow 边界分别是:
|
||
|
||
1. [npm publication](../.github/workflows/npm-publication.yml) 是唯一的 Trusted Publisher
|
||
caller;它只能从 `main` 运行,把精确产品 tag 作为待验证的数据路由正式发布,并发布 npm Nightly;
|
||
2. [Stage CLI npm release](../.github/workflows/release-cli-stage.yml) 解析已有的产品 tag 与 GitHub
|
||
Release;无 OIDC 的 job 从产品 commit 构建并验证一个 immutable tarball;OIDC job 只执行
|
||
`main` 上已审查的 publisher 代码,分别记录产品 source 与 publisher 身份,再把验证过的字节提交到 npm staging;
|
||
3. [Desktop Nightly](../.github/workflows/desktop-nightly.yml) 只在 npm Nightly 成功后启动,只消费
|
||
immutable version file;source commit 与上游 run 身份直接取自已认证的
|
||
`workflow_run` event;
|
||
4. [Finalize product release](../.github/workflows/release-cli-finalize.yml) 只接受精确的成功
|
||
Stage run、Release build run 及其自包含 publication record;`main` 上当前已审查的 verifier
|
||
验证公共 registry 字节、signature、provenance、dist-tag、不可变 build artifacts 与 live Draft
|
||
digest,然后等待受保护的 `product-release` Environment;独立 Desktop 验收完成并批准后,它会
|
||
用受保护 workflow 的身份证明精确便利包,并在同一个操作中发布 GitHub Release 及其 Stable/Latest 分类。
|
||
|
||
## 一次性控制面配置
|
||
|
||
### GitHub Environment
|
||
|
||
仓库中的 `.asf.yaml` 是 `npm-publication`、`nightly` 和 `product-release` Environment 的权威。该配置进入 `main` 后,确认 ASF
|
||
同步出的 live 配置满足:
|
||
|
||
- `npm-publication` 只允许 selected `main` branch;为保证 Nightly 自动运行,不设置
|
||
approval gate;
|
||
- `nightly` 只允许 selected `main` branch;为保证 Desktop Nightly 自动运行,不设置 approval gate;
|
||
- `product-release` 只允许 selected `main` branch,required reviewer 为 `M4n5ter`,并禁止
|
||
self-review;
|
||
- 仓库策略允许时禁用 administrator bypass。
|
||
|
||
检查或修复同步结果需要仓库 administration 权限;不要再在 GitHub UI 中维护第二套手工
|
||
Environment policy。Finalize 使用 GitHub Actions OIDC 为精确便利包生成 Sigstore provenance,
|
||
并把离线验证 bundle 与便利包放在一起;不需要仓库 administration credential、签名私钥或 npm token。
|
||
|
||
### npm Trusted Publisher
|
||
|
||
在 `maka-agent` package settings 中配置一个 GitHub Actions trusted publisher:
|
||
|
||
| 字段 | 值 |
|
||
| --- | --- |
|
||
| Organization or user | `apache` |
|
||
| Repository | `maka` |
|
||
| Workflow filename | `npm-publication.yml` |
|
||
| Environment name | `npm-publication` |
|
||
| Allowed actions | `npm publish` 与 `npm stage publish` |
|
||
|
||
Workflow filename 区分大小写,并且不包含 `.github/workflows/` 前缀。正式 staging 与 Nightly
|
||
direct publish 都使用 `npm-publication` Environment;它只允许 `main`。因为 Nightly 需要自动
|
||
运行,所以不设置 GitHub approval gate;正式发布在 staging 后仍须由
|
||
人工使用 npm 2FA 批准。不要配置第二个 publisher 或 npm token。
|
||
|
||
第一次 OIDC Stage 成功后,将 package publishing access 设置为 **Require two-factor
|
||
authentication and disallow tokens**,然后撤销不再使用的 publish token。不要在这一步移除
|
||
人工 package owner 或恢复权限。
|
||
|
||
在 `.asf.yaml` 完成 Environment 同步且 Trusted Publisher 与其匹配前,不要设置仓库变量
|
||
`NPM_NIGHTLY_ENABLED`。之后将它设为 `true`,先手动运行一次 Nightly,再依赖 schedule。该变量
|
||
只控制 npm;Desktop 使用独立的 `DESKTOP_NIGHTLY_ENABLED` rollout gate。
|
||
|
||
## Product Nightly
|
||
|
||
定时 `npm-publication.yml` run 会从精确的 `main` commit 生成一个类似
|
||
`0.2.0-dev.42.20260829` 的 immutable 版本,验证四平台 `maka-agent` tarball 并发布到
|
||
`nightly`。只有精确版本和 dist-tag 已公开后,成功的 workflow 才会触发
|
||
`desktop-nightly.yml`。Desktop 只消费该版本;精确 source commit 来自已认证的上游 event。
|
||
打包后的 Desktop 记录精确的 Runtime Host setup specifier,例如
|
||
`maka-agent@0.2.0-dev.42.20260829`,绝不安装会漂移的 `nightly` tag。
|
||
|
||
两个 workflow 按以下顺序发布:
|
||
|
||
1. 要求候选 npm run number 大于当前 `nightly` tag;
|
||
2. 使用 provenance 将精确 npm tarball 发布到 `nightly`;
|
||
3. 要求公共 registry 中的精确版本和 `nightly` tag 都已可读;
|
||
4. 构建、验证并 attest 精确的 Desktop 安装包和 GitHub `dev` metadata;
|
||
5. 将受保护的 `v<version>` tag 绑定到精确 source commit,并验证 Draft 中全部九项资产;
|
||
6. 仅在 Draft 完整后发布 Latest 关闭的 GitHub prerelease。
|
||
|
||
这个顺序既避免 Desktop 指向 npm 中不存在的 Runtime Host,也让 npm Nightly 与 Desktop 打包彼此
|
||
独立。npm 或 Desktop run 失败后都不得原地 rerun;应启动新的 npm Nightly:
|
||
|
||
```sh
|
||
gh workflow run npm-publication.yml --ref main -f channel=nightly
|
||
```
|
||
|
||
Nightly 是开发快照,不是 Apache Release,不得从面向最终用户的下载页推广。开发者可以明确使用
|
||
`maka-agent@nightly`;产品自动化必须使用 Desktop 记录的精确版本。
|
||
|
||
## 准备发布
|
||
|
||
1. 将本次包、文档和发布变更全部合并到 `main`,准备 ASF source candidate,并完成 podling 和 Incubator PMC 两轮投票;
|
||
2. 确认已批准 source commit 上的根产品版本、`apps/desktop/package.json` 与
|
||
`packages/cli/package.json` 是同一个尚未使用的稳定目标版本。正式 npm 发布只推进 `latest`;
|
||
3. 从精确的已批准 `v<version>-incubating-rc<rc>` tag dispatch 产品 `Release` workflow,并将同一个 tag 作为 `source_reference_tag`。确认其 Draft `v<version>` Release 指向已批准 commit;npm staging 消费这个身份,不能先于它运行;
|
||
4. 确认目标版本既不在公共 registry,也不在 staged package 中:
|
||
|
||
```sh
|
||
version=0.2.0
|
||
npm view "maka-agent@$version" version --registry https://registry.npmjs.org/
|
||
npm stage list maka-agent --registry https://registry.npmjs.org/
|
||
```
|
||
|
||
第一个命令应报告目标版本不存在。如果已经存在同版本 stage,先处理它,不要再次提交;
|
||
5. 确认 `npm-publication` Environment 和 Trusted Publisher 仍与上面的值一致,并确认负责批准的
|
||
npm 账号已经启用 2FA。
|
||
|
||
## Stage 候选包
|
||
|
||
1. 从已审查的 `main` dispatch workflow,并提供精确产品版本:
|
||
|
||
```sh
|
||
version=0.2.0
|
||
gh workflow run npm-publication.yml --ref main \
|
||
-f channel=formal \
|
||
-f version="$version"
|
||
```
|
||
|
||
2. 确认新建的 run 使用 `main`。workflow 把 `v<version>` 与 Draft 作为数据解析,要求 tag
|
||
commit 仍是 `main` 的 ancestor,在无 OIDC 的 job 构建候选包,并把 npm provenance 绑定到
|
||
已审查的 `main` publisher workflow 与精确 run;
|
||
3. 等待可复用 package validation jobs 全部通过。它们只构建一个 tarball,并在 Linux x64/arm64、
|
||
macOS arm64、Windows x64 上验证安装态 CLI,在 Linux x64 上运行真实 Harbor 和 Pier
|
||
Docker cell;
|
||
4. 从 run summary 和 `cli-staged-release-<attempt>` artifact 记录成功 Stage workflow 的 run
|
||
ID、run attempt、source commit、version 和 staged artifact checksum。
|
||
|
||
Stage workflow 没有成功结束时,不得在 npm 上批准任何内容。
|
||
|
||
## 在 npm 上检查并批准
|
||
|
||
执行下面的检查和审批命令需使用 Node.js 22.14.0 或更高版本和 npm 11.15.0 或更高版本。Stage workflow 使用自身经过审查的工具链:workflow 固定的 Node.js 版本,以及仓库 `packageManager` 固定的精确 npm 版本。
|
||
|
||
```sh
|
||
npm stage list maka-agent --registry https://registry.npmjs.org/
|
||
stage_id=replace-with-reviewed-stage-id
|
||
npm stage view "$stage_id" --registry https://registry.npmjs.org/
|
||
npm stage download "$stage_id" --registry https://registry.npmjs.org/
|
||
```
|
||
|
||
批准前必须:
|
||
|
||
- 确认 package name、version、dist-tag、provenance 和 source repository 与 Stage run 一致;
|
||
- 将下载的 staged tarball SHA-256 与 workflow artifact 的 `.tgz.sha256` 比较;
|
||
- 检查文件清单和包内 `README.md`;
|
||
- 确认 tarball 属于所记录的 Stage run 和 source commit。
|
||
|
||
批准前的最后一步,重新检查 Stage run 记录的 live 产品权威:
|
||
|
||
```sh
|
||
set -eu
|
||
source_commit=replace-with-stage-recorded-commit
|
||
node scripts/product-release-authority.mjs verify-draft \
|
||
"v$version" "$source_commit" apache/maka
|
||
```
|
||
|
||
verifier 必须成功。tag 不存在、已移动、不再位于 `main`,匹配的 GitHub Release 不再是 Draft,
|
||
或被标记为 prerelease 时都必须停止。
|
||
|
||
只批准这个 stage ID。npm 会要求 2FA,并在批准时将 package 公开:
|
||
|
||
```sh
|
||
npm stage approve "$stage_id" --registry https://registry.npmjs.org/
|
||
```
|
||
|
||
也可以在 npmjs.com package 的 **Staged Packages** 页面完成相同的检查和批准。
|
||
|
||
获得批准后,检查公共 dist-tags:
|
||
|
||
```sh
|
||
version=0.1.0
|
||
npm view maka-agent dist-tags --json --registry https://registry.npmjs.org/
|
||
```
|
||
|
||
`latest` 必须指向获批版本;`nightly` 如果存在,保持独立。
|
||
|
||
## Finalize 产品发布
|
||
|
||
npm 显示该版本已经公开后:
|
||
|
||
1. 在 `main` 上打开 **Actions → Finalize product release → Run workflow**;
|
||
2. 输入成功 Stage 的 run ID 与精确 attempt、成功 Release build 的 run ID 与精确 attempt,以及
|
||
version;
|
||
3. 让 inspection job 验证公共 tarball 字节、checksum、inventory、npm signature、Trusted
|
||
Publishing provenance 与精确的 `latest` dist-tag;
|
||
4. publication job 等待 `product-release` 批准期间,针对 Draft 完成产品检查清单中的跨机器验收;
|
||
5. 批准 Environment,并确认 workflow 将每个 live Draft digest 与精确 Release attempt 的
|
||
publication record 对比,生成并上传 `Maka-<version>-attestation.sigstore.json`,发布便利包
|
||
Release;stable release 会同时成为 Latest,不再需要单独人工操作。
|
||
|
||
检查最终 registry 状态:
|
||
|
||
```sh
|
||
version=0.2.0
|
||
npm view "maka-agent@$version" version dist.tarball dist.integrity --json
|
||
npm view maka-agent dist-tags --json
|
||
```
|
||
|
||
最后,在每个发布平台安装精确的公共版本,并完成一次真实的 TUI/model turn。在支持的 Eval
|
||
host 上完成至少一个真实 experiment cell,检查 score、usage、cost 和 artifacts。
|
||
|
||
[产品发布检查清单](../.github/RELEASE_CHECKLIST.md)仍是批准发布前所需验收证据的权威。
|
||
|
||
## 失败恢复
|
||
|
||
### npm staging 之前失败
|
||
|
||
如果在 `npm stage publish` 前发生瞬时失败,从同一个产品 tag 重新运行 Stage。如果必须修改代码或 workflow,则在 `main` 修复、递增产品版本、创建新的产品 tag 和 Draft,再 Stage 新版本。此时没有消耗 npm 版本。
|
||
|
||
### Stage workflow 失败,但 npm 中存在 stage
|
||
|
||
提交是 Stage workflow 的最后一个业务步骤,因此响应丢失可能导致 workflow 未成功但 npm
|
||
已经存在 stage。不要批准这个 orphan:Finalize 只接受成功的 Stage run attempt。
|
||
|
||
检查后,先用 2FA 拒绝精确的 stage ID,再启动新的 Stage run:
|
||
|
||
```sh
|
||
stage_id=replace-with-reviewed-stage-id
|
||
npm stage view "$stage_id" --registry https://registry.npmjs.org/
|
||
npm stage reject "$stage_id" --registry https://registry.npmjs.org/
|
||
```
|
||
|
||
不要只根据 version 文本拒绝 stage;操作必须绑定到已经检查的 stage ID。
|
||
|
||
### Stage 成功,但人工检查发现问题
|
||
|
||
拒绝该 stage,在 `main` 修复、递增产品版本、创建新的产品 tag 和 Draft,再 Stage 新版本。不要为了清空 staging area 而批准有问题的候选。
|
||
|
||
### npm approval 成功,但 Finalize 失败
|
||
|
||
npm 版本此时已经 immutable,不要再次 publish 或 approve。保留 Stage run ID、attempt、version,
|
||
以及 Release run ID、attempt、publication record 和 artifacts。如果这些字节与 provenance
|
||
有效,在 `main` 修复 Finalize verifier,然后针对同一组不可变 Stage 与 Release 证据重新运行。
|
||
|
||
inspection job 只读;只有受保护的 publication job 可以执行一次 Draft-to-published 转换并证明其字节。如果
|
||
npm identity、build evidence 或 Draft 不一致,立即停止并调查;不要修改产品 tag 或 GitHub
|
||
Release 来让验证通过。如果错误发生在 publication request 之后,先检查精确 Release;已经成功
|
||
完成的发布不得重复执行。
|
||
|
||
### 公共版本存在缺陷
|
||
|
||
先把受影响的 dist-tag 指回先前验证过的版本:
|
||
|
||
```sh
|
||
known_good=0.2.0
|
||
npm dist-tag add "maka-agent@$known_good" latest
|
||
```
|
||
|
||
然后只 deprecate 有缺陷的版本,并引导用户使用已经指向验证版本的恢复 dist-tag:
|
||
|
||
```sh
|
||
bad_version=0.2.1
|
||
recovery_tag=latest
|
||
npm deprecate "maka-agent@$bad_version" "Known issue; install maka-agent@$recovery_tag."
|
||
```
|
||
|
||
验证 dist-tags、修复缺陷,然后通过完整 Stage 和 Finalize 流程发布新版本。不要把
|
||
`npm unpublish` 当作常规回滚:删除 immutable dependency bytes 会破坏现有安装,也不能恢复
|
||
经过审查的发布链。
|
||
|
||
Nightly 存在缺陷时,dispatch 新 run,让新的 immutable 版本推进 `nightly`。如果必须立即回滚,
|
||
人工 package owner 可以把 `nightly` 指回之前验证过的 Nightly,并只 deprecate 有缺陷的精确
|
||
版本。绝不能让 `latest` 指向 Nightly。
|
||
|
||
## 所有权和紧急恢复
|
||
|
||
- GitHub repository admin 负责 `npm-publication` Environment 配置;release maintainer 负责
|
||
dispatch、staged-package 检查、npm 2FA approval 和最终验收;
|
||
- npm package owner 负责 Trusted Publisher、publishing access、maintainer 和 dist-tag 恢复;
|
||
- trusted publishing 启用期间,至少保留一个启用 2FA 的人工 owner。移除当前 direct owner
|
||
前,先加入预期的 npm organization publishing team 和另一名人工 direct recovery
|
||
maintainer,并验证两条恢复路径;
|
||
- workflow 不得获得长期 npm token。OIDC、Environment 或 trust relationship 损坏时,暂停
|
||
发布并修复控制面,不要使用 `npm publish` 绕过 staging;
|
||
- npm 账号丢失时,使用该账号的恢复方式或另一名已经验证的 package owner。在建立第二名
|
||
owner 之前,恢复依赖当前 owner 的 npm recovery credential;完成所有权 follow-up 属于
|
||
明确的运维债务;
|
||
- repository 或 npm publisher 设置出现意外变更时,移除或禁用 trust relationship,保留
|
||
workflow 与 npm audit 证据,恢复经过审查的配置,并为 integrity 存疑的候选使用新版本。
|
||
|
||
## 参考资料
|
||
|
||
- [ASF Incubator npm 分发指南](https://incubator.apache.org/guides/distribution.html#npm)
|
||
- [Apache OpenDAL 孵化期 Node.js 发布 workflow](https://github.com/apache/opendal/blob/v0.44.0/.github/workflows/bindings_nodejs.yml)
|
||
- [Apache OpenDAL 孵化期发布指南](https://github.com/apache/opendal/blob/v0.44.0/website/community/committers/release.md)
|
||
- [npm staged publishing](https://docs.npmjs.com/staged-publishing/)
|
||
- [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/)
|
||
- [npm dist-tags](https://docs.npmjs.com/cli/dist-tag/)
|
||
- [npm deprecation](https://docs.npmjs.com/cli/v11/commands/npm-deprecate/)
|
||
- [GitHub deployment environments](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments)
|