Skip to content

发布原生包

napi-rs 以 npm 包形式分发预构建插件。使用者安装一个很小的根包,包管理器会根据当前操作系统、CPU 和 libc 选择匹配的可选包。使用者的机器无需编译器或安装时下载脚本。

WARNING

多平台发布不是原子操作。npm 版本不可变,而故障可能发生在部分平台包已经存在、根包尚未发布之间。请把发布任务视为生产变更,而不是构建预览。

分发模型

对于 @scope/addon 这样的根包,napi-rs 会创建:

text
@scope/addon
@scope/addon-darwin-arm64
@scope/addon-win32-x64-msvc
@scope/addon-linux-x64-gnu
@scope/addon-linux-x64-musl

每个平台包包含一个原生产物,并声明 npm oscpu 以及适用时的 libc 限制。根包在 optionalDependencies 中列出这些包的精确版本;其生成的加载器随后加载与运行系统匹配的包。

该模型避免了两种常见替代方案:

  • 分发 Rust/C/C++ 源码,并要求每个使用者安装原生工具链。
  • postinstall 中从 GitHub 或 CDN 下载二进制文件,这会带来安装时网络和私有网络故障。

发布流水线中的命令

命令 职责
napi create-npm-dirs 为每个已配置目标创建包目录。
napi build 每次调用构建一个目标。CI 为每个矩阵任务运行一次。
napi artifacts 将下载的 .node/.wasm 文件收集到根包与平台包。
napi pre-publish 同步版本和可选依赖,发布平台包,并可选地创建/上传 GitHub release。
npm publish 发布根包。在模板中,它会先通过 prepublishOnly 调用 napi prepublish -t npm

napi pre-publish 不会构建或收集产物,也不会自行发布根包。

一次性发布设置

首次发布前:

  1. 使用 npm scope,或确认根包名及所有带目标后缀的包名都可用。
  2. package.json 中设置最终的 namerepositorylicensepublishConfig。为了 npm provenance,repository 必须与 GitHub 工作流匹配。
  3. 检查 napi.binaryNamenapi.targets。每个目标都需要包、构建任务和运行时测试;仅仅是可接受的 target triple 并不构成支持保证。
  4. 将 npm automation token 配置为 NPM_TOKEN Actions secret;除非你有意用 npm trusted publishing 替换模板。该身份必须有权发布根包和所有平台包名。
  5. 在发布任务中保留用于创建 GitHub release 的 contents: write,以及用于 npm provenance 的 id-token: write
  6. 启用发布前,先让普通分支/PR 工作流成功运行。

扩展生成的矩阵前,请阅读支持与兼容性交叉编译

每个版本的发布前检查

创建版本提交前,请确认:

  • 发布提交来自预期的干净分支,且源代码已完成审核。
  • 本地格式化、Rust 检查、JavaScript 测试、生成声明和本地原生加载全部通过。
  • CI 矩阵构建 napi.targets 中的每一项,而且每个生成文件都具有预期的 binaryName.platform-arch-abi 后缀。
  • 新的根包和平台包版本在 npm 上都不存在。
  • npm whoami 对发布身份执行成功,且 token 对所有包名都有效。
  • changelog 与 Node-API/运行时支持声明符合本次发布。

在不运行生命周期脚本的情况下检查根 tarball:

sh
npm pack --dry-run --ignore-scripts

不要依赖 npm publish --dry-run:npm 生命周期脚本仍可能调用 napi prepublish,从而真正发布平台包。请单独使用 napi pre-publish --dry-run,同时注意它不会验证产物完整性或 registry 授权。

使用生成的工作流发布

持续维护的模板从 GitHub Actions 工作流发布。该任务会:

  1. 等待 lint、构建和运行时测试任务。
  2. 使用 actions/download-artifact@v8 下载所有工作流产物。
  3. 创建目标 npm 目录。
  4. 运行 napi artifacts 填充根包与平台包。
  5. 启用 npm provenance。
  6. 对根包运行 npm publish。它的 prepublishOnly 脚本运行 napi prepublish -t npm,先发布平台包并上传 GitHub release 产物。
  7. 使用默认 npm tag 发布稳定版本,或使用 next tag 发布预发布版本。

当前模板根据最新提交消息决定是否发布。npm version 默认就会把裸版本号写入提交消息(其 message 配置默认值为 %s);只有 Git tag 会带上 v 前缀(tag-version-prefix 默认为 v),而模板的发布关卡对 1.2.3v1.2.3 都接受。因此仅 npm version patch 本身产生的提交消息就能匹配该关卡——下面传入 -m "%s" 是可选的,只是固定提交消息格式:

sh
# Creates the version commit and v-prefixed Git tag, but makes the commit
# message itself exactly the new version (for example, 1.2.3).
npm version patch -m "%s"
git push --follow-tags

预发布版本:

sh
npm version prerelease --preid next -m "%s"
git push --follow-tags

使用这些命令前,请检查生成的 .github/workflows/CI.yml。如果项目已经更改触发器或发布工具,应遵循签入仓库的工作流,而不是模板约定。

CI 中的发布关卡

由于预期目标文件缺失时 CLI 只会警告并继续,请在发布步骤之前添加明确关卡,证明:

  • 每个已配置目标目录都存在。
  • 每个目录包含且只包含预期的 .node.wasm 文件。
  • WASI 包包含生成的加载器和 worker 支持文件。
  • 没有产物使用意外的二进制名称或目标后缀。
  • 平台运行时测试使用的正是即将发布的产物。

除非所有平台关卡都通过,否则不要发布根包。根版本一旦存在,客户端可能立即尝试解析每个列出的可选依赖。

验证已发布版本

工作流显示绿色并不足够。发布后:

  1. npm view @scope/addon@<version> --json 读取根包元数据,确认 dist-tag 和精确 optionalDependencies
  2. 查询同一版本的每个平台包,检查其 oscpulibc 和 tarball 文件列表。
  3. 工作流承诺 provenance 时,确认 npm 显示该信息。
  4. 确认 GitHub release 指向预期 tag,并包含所有预期二进制产物。
  5. 在有代表性的 glibc、musl、macOS 和 Windows 系统上将根包安装到干净项目,并调用一个原生导出。
  6. 发布包含 WASI 时,单独测试原生到 WASI 的回退。

将发布工作流 URL 和验证结果保存在 release notes 中。

从部分发布中恢复

不要立即提升版本或重新构建。先盘点失败版本的 npm 平台包、根包和 GitHub 产物。已发布二进制文件绝不能在同一版本下被不同内容替换。

恢复工具包括:

  • 使用未更改的产物重新运行 napi prepublish -t npm,发布缺失的平台包。npm 返回标准重复版本错误时,已发布版本会被跳过。
  • 传入 --gh-release-id <id>,上传到现有 release,而不是另建一个。
  • 只有在独立确认每个平台包都已存在后才传入 --skip-optional-publish
  • 如果只剩根包未发布,请在可信发布任务中禁用生命周期脚本并发布未更改的根 tarball,避免重复平台阶段。

请遵循详细的部分失败与恢复流程。如果根包在缺失某个平台依赖时已经发布,请立即发布缺失包,或弃用损坏的根版本;npm 没有原子回滚。