npm Classic Token 作废后,CI/CD 自动发包如何改?一份完整踩坑复盘

背景

最近在给 npm 包做 CI 发版时,突然开始频繁失败,npm 的提示也非常直白:

Classic tokens have been revoked.
Granular tokens are now limited to 90 days and require 2FA by default.
Update your CI/CD workflows to avoid disruption.

一句话总结就是:

老的 npm token 方案,已经不适合 CI 了。

现在就记录一次完整的 CI/CD 改造过程,包含:

  • npm 新规则到底改了什么
  • CI 为什么会突然发布失败
  • 过程中遇到的几个"必踩坑"
  • 最终一套可长期运行的 GitHub Actions 发包方案

一、npm 这次到底动了谁的蛋糕?

先说结论:

npm 正在彻底废弃"长期有效 token + npm login"的发布模型。

核心变化有三点。

1. Classic Token 作废

以前最常见的做法是:

yaml 复制代码
env:
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

这种 Classic Token

  • 长期有效
  • 没有明确权限边界
  • 一旦泄露,后果很严重

现在 npm 明确表态:
Classic Token 不再推荐,并会逐步失效。


2. Granular Token 默认 90 天 + 2FA

新的 Granular Token:

  • 必须开启 2FA
  • 默认 90 天过期
  • 非常不适合无人值守的 CI

👉 这意味着:
即使你换成 Granular Token,CI 依然不稳定。


3. npm 开始强制推广 provenance(供应链校验)

这是最关键、也是最容易被忽略的一点。

npm 现在会校验:

  • 这个包是不是在 GitHub Actions 里发布的
  • 发布它的仓库是谁
  • package.json.repository 指向哪里
  • GitHub OIDC 身份是否匹配

校验失败,直接拒绝发布。


二、为什么"明明登录了",还是发布失败?

我们最初遇到的错误包括:

❌ ENEEDAUTH

vbnet 复制代码
npm error need auth This command requires you to be logged in

原因很简单:

  • CI 里已经不再支持老的登录方式
  • token 被判定为过期或 revoked

❌ 404 / E404

kotlin 复制代码
404 '@scope/package@x.y.z' is not in this registry

这个错误非常迷惑,但本质上是:

npm 拒绝了你的发布请求,并不是包不存在。


❌ 422 Unprocessable Entity(最坑)

vbnet 复制代码
Error verifying sigstore provenance bundle:
package.json: "repository.url" is ""
expected to match "https://github.com/infinilabs/ci"

这一步,几乎是所有人都会踩的坑。


三、npm provenance 校验在校验什么?

npm 的逻辑是:

我不只要你能发布,我还要知道你是从哪里发布的。

它会做一组严格匹配:

校验项 来源
GitHub Actions 所在仓库 CI
OIDC 身份 GitHub
package.json.repository.url 包元数据

只要有一个不一致:

👉 422,拒绝发布。


四、为什么构建产物里的 package.json 会出问题?

我们的发布流程是:

bash 复制代码
pnpm run build:web
cd out/search-chat
npm publish

注意重点:

真正 publish 的不是源码目录,而是构建产物目录。

很多项目在 build 后:

  • package.json 是重新生成的
  • repository 为空
  • 或指向源码仓库,而不是 CI 仓库

这在 npm provenance 时代,直接就是死刑。


五、正确的 CI 改造思路

目标很明确:

不用 npm token,不用 npm login,让 CI 自己完成身份认证。

核心方案是:

✅ GitHub Actions + OIDC + npm provenance


六、关键改造点一:npm 账户设置以及包设置

npm 账户设置:

npm 对应包设置:


七、关键改造点二:开启 OIDC 权限

在 workflow 顶部增加:

yaml 复制代码
permissions:
  contents: read
  id-token: write

这一步非常关键:

  • id-token: write 是 npm provenance 的前提
  • 没有它,npm 无法验证 CI 身份

八、关键改造点三:彻底移除 npm token

我们做了三件事:

  • 删除 NODE_AUTH_TOKEN
  • 删除 .npmrc
  • 不再执行 npm login

CI 里只保留:

复制代码
npm publish

GitHub Actions 会自动用 OIDC 身份和 npm 通信。


九、关键改造点四:修正构建产物里的 repository

在真正 publish 前,强制修正 package.json

vbnet 复制代码
jq '.repository = {
  "type": "git",
  "url": "https://github.com/infinilabs/ci"
}' package.json > tmp.json && mv tmp.json package.json

注意几个要点:

  • URL 必须是当前 CI 所在仓库
  • 不能是源码仓库
  • 不能为空
  • 必须是 https GitHub 地址

十、最终发布流程(核心片段)

bash 复制代码
pnpm run build:web
cd out/search-chat

# 修正 repository
jq '.repository = {
  "type": "git",
  "url": "https://github.com/infinilabs/ci"
}' package.json > tmp.json && mv tmp.json package.json

# 发布
npm publish

发布成功时,npm 会输出:

  • provenance 已签名
  • 已写入 sigstore transparency log

十一、为什么不直接关 provenance?

npm 提供了:

css 复制代码
npm publish --no-provenance

但这只是一个 临时选项

  • 官方趋势是默认开启
  • 未来可能直接强制
  • CI 早适配,后面少折腾

十二、这次改造后的收益

最终我们得到的是一套:

  • ✅ 无 token
  • ✅ 无 2FA 人工参与
  • ✅ 不会过期
  • ✅ 可审计、可追溯
  • ✅ 符合 npm 官方长期路线

真正意义上的 无人值守 CI 发包


十三、小结

npm 不再相信"你是谁",

它只相信 你从哪里来

在这个前提下,
OIDC + provenance,不是可选项,而是必选项。

相关推荐
怣疯knight2 小时前
windows比较好用的翻译软件
开源·github
YMGogre2 小时前
GitHub 仓库管理员
git·github
古城小栈2 小时前
GitHub Copilot for Java:上下文感知重构建议实操
java·github·copilot
CoderJia程序员甲2 小时前
GitHub 热榜项目 - 日榜(2025-12-23)
ai·开源·大模型·github·ai教程
亚林瓜子3 小时前
AWS Lambda 添加NodeJS依赖库层
npm·云计算·nodejs·node·aws·lambda
于谦3 小时前
✨ feat(app1,pkg1): monorepo生成规范化提交信息的最优解 - committier
javascript·github·代码规范
2301_805962934 小时前
Android Studio 图标制作与汉化全攻略:从生成图标到汉字图标、中文应用名
gitee·github
Xinstall渠道统计平台5 小时前
如何利用APP渠道统计提升营销效果
java·git·github
幺零九零零5 小时前
全栈程序员-前端第一节-npm 是什么?
前端·npm·node.js