核心是将 npm publish 变为可验证、可回溯、有明确触发条件的流程:仅在 git tag 推送时触发,分 build 与 publish 两阶段,显式指定 registry 和 token,用 dist-tag 隔离测试版(如 beta)与正式版(latest),确保安全可控。

用 npm publish 配合 CI 自动发布新版本,核心不是“让 CI 直接跑 npm publish”,而是把发布动作变成可验证、可回溯、有明确触发条件的流程。关键在于:版本号生成、构建产物准备、标签管理、权限安全、失败兜底这五件事必须在 CI 中闭环。
触发时机要明确:只在打 Git Tag 时发布
CI 不该监听 main 分支推送就自动发版——那会把未验证的代码直接推到 latest。正确做法是:
- 开发完功能后,本地执行 npm version patch(或 minor/major),它会自动更新 package.json、生成 git commit 并打 tag,例如 v1.2.3
- 把 commit 和 tag 一起推到远程:git push --follow-tags
- CI 配置只监听 git tag 事件(如 GitHub Actions 的
on: push: tags: ['v*']),匹配上才启动发布流程
构建与发布必须分离:先 build,再 publish
CI 流程里不能一边构建一边发布。要拆成两个阶段:
- Build 阶段:安装依赖、运行测试、执行构建脚本(如 tsc / rollup / tsup)、产出 dist/ 目录;校验产物完整性(比如检查 dist/index.js 是否存在、是否可 require)
- Publish 阶段:仅做一件事——npm publish --tag=latest(正式版)或 npm publish --tag=next(预发布版)。不重复构建,不修改代码
这样设计的好处是:build 失败不会污染 registry;publish 失败可重试,不影响产物一致性。
权限与源必须显式指定,不依赖本地配置
CI 环境没有 npm login 状态,也不能信任 .npmrc 文件。必须显式传参:
- 用 --registry https://registry.npmjs.org/ 强制走官方源,避免镜像源拒绝 publish
- 用 --auth-token ${{ secrets.NPM_TOKEN }} 传入 token(GitHub Secrets 中预存的 npm 个人访问令牌,权限为 “Automation”)
- 不要用 npm login 或 .npmrc,token 过期或权限不足时会立刻失败,便于定位
正式版和测试版要用 dist-tag 隔离
避免“一发就全量升级”,必须区分用户场景:
- 日常预发布用 npm publish --tag=beta 或 --tag=next,下游项目需显式写
"my-pkg": "beta"才能装到 - 确认无误后,再对同一版本号执行 npm dist-tag add my-pkg@1.2.3 latest,把它设为默认安装版本
- CI 可以在 tag 名匹配
v*且分支为 main 时走 latest;匹配beta-v*时走 beta 标签
这样即使发错,也只影响主动 opt-in 的测试项目,不会波及所有消费者。











