使用 npm Trusted Publishing 通过 GitHub Actions 自动发布 npm 包

1932 字
10 分钟
使用 npm Trusted Publishing 通过 GitHub Actions 自动发布 npm 包

npm Trusted Publishing 允许 GitHub Actions 通过 OIDC 身份发布 npm 包,不再需要长期保存 NPM_TOKENNODE_AUTH_TOKEN 这类发布 token。

官方文档:

https://docs.npmjs.com/trusted-publishers/

它解决的核心问题很直接:

让 npm 只信任指定仓库、指定 workflow 发起的发布动作,而不是依赖一个长期有效的发布 token。

适用场景#

Trusted Publishing 适合这些项目:

  • npm 包由 GitHub Actions 自动发布。
  • 不想在 GitHub Secrets 里保存长期 npm 发布 token。
  • 发布动作只应该由指定仓库、指定 workflow 执行。
  • 希望发布过程带有 npm provenance / OIDC 信任链。

它不适合这些场景:

  • 使用 self-hosted runner 发布。npm Trusted Publishing 当前主要支持 GitHub-hosted runner。
  • 发布时需要访问私有 npm 依赖,但没有额外配置只读 token。
  • 想在本地完整模拟 OIDC 发布。OIDC 鉴权只能在真实 CI 环境里完整验证。

基本原理#

Trusted Publishing 的流程可以理解成五步:

  1. 在 npm 包设置里声明“这个 GitHub 仓库的某个 workflow 可以发布这个包”。
  2. GitHub Actions workflow 开启 id-token: write 权限。
  3. npm publish 在 CI 中自动检测 OIDC 环境。
  4. npm 校验 workflow 身份是否和包设置里的 Trusted Publisher 匹配。
  5. 校验通过后发布包,不需要 NPM_TOKEN

也就是说,真正的发布凭据不再是你手动生成的长期 token,而是 CI 运行时临时生成的 OIDC 身份。

前置要求#

按照 npm 官方文档,Trusted Publishing 当前要求:

  • Node.js 22.14.0 或更高。
  • npm CLI 11.5.1 或更高。
  • workflow 运行在支持的 CI 环境中,例如 GitHub-hosted runner。
  • npm 包页面已经配置 Trusted Publisher。

为了省事,GitHub Actions 里可以直接使用 Node 24:

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"

第一步:在 npm 包里配置 Trusted Publisher#

进入 npm 包页面:

https://www.npmjs.com/package/<你的包名>/access

找到 Trusted Publisher,选择 GitHub Actions,然后填写:

Organization or user: GitHub 用户名或组织名
Repository: 仓库名
Workflow filename: publish.yml
Environment name: 留空,除非 workflow 使用了 GitHub Environment
Allowed actions: npm publish

注意:

  • Workflow filename 只填文件名,例如 publish.yml
  • 不要填 .github/workflows/publish.yml
  • workflow 文件必须真实存在于 .github/workflows/ 目录下。
  • monorepo 中有多个 npm 包时,每个要发布的包都需要单独配置 Trusted Publisher。

如果这些字段和真实 workflow 不一致,通常要等到发布时才会暴露问题。npm 不会在保存 Trusted Publisher 配置时替你完整验证仓库和 workflow 是否真的匹配。

第二步:配置 GitHub Actions workflow#

单包项目可以使用下面这个最小版本:

name: Publish
on:
push:
tags:
- "v*"
permissions:
id-token: write
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build --if-present
- name: Test
run: npm test --if-present
- name: Publish to npm
run: npm publish --access public

关键点是:

permissions:
id-token: write
contents: read

以及发布时使用:

Terminal window
npm publish --access public

不要再传:

env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Trusted Publishing 要的不是长期 token,而是 GitHub Actions 在运行时签发的 OIDC 身份。

Yarn / pnpm 项目怎么处理#

即使项目使用 Yarn 或 pnpm,也建议发布步骤使用 npm publish

例如 Yarn 项目:

- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: yarn install --immutable
- name: Build
run: yarn build
- name: Publish to npm
run: npm publish --access public

pnpm 项目同理:

- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Publish to npm
run: npm publish --access public

原因是 Trusted Publishing 是 npm CLI 的 OIDC 发布能力。安装和构建可以继续沿用项目自己的包管理器,但发布动作建议交给 npm CLI。

Monorepo 示例#

如果一个仓库里有多个包,可以用 tag 决定发布哪个 workspace。

例如 tag 格式:

workflow 示例:

name: Publish
on:
push:
tags:
- "**"
permissions:
id-token: write
contents: read
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Parse tag
id: parse
run: |
TAG="${GITHUB_REF#refs/tags/}"
PACKAGE_NAME="${TAG%@*}"
VERSION="${TAG##*@}"
echo "package_name=$PACKAGE_NAME" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false
- name: Enable Corepack
run: corepack enable
- name: Install dependencies
run: yarn install --immutable
- name: Find workspace
id: find
run: |
PACKAGE_DIR=$(yarn workspaces list --json \
| jq -r --arg name "${{ steps.parse.outputs.package_name }}" \
'select(.name == $name) | .location')
if [ -z "$PACKAGE_DIR" ]; then
echo "::error::Package not found: ${{ steps.parse.outputs.package_name }}"
exit 1
fi
echo "dir=$PACKAGE_DIR" >> "$GITHUB_OUTPUT"
- name: Verify version
working-directory: ${{ steps.find.outputs.dir }}
run: |
PKG_VERSION=$(jq -r '.version' package.json)
TAG_VERSION="${{ steps.parse.outputs.version }}"
if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Version mismatch: package.json=$PKG_VERSION tag=$TAG_VERSION"
exit 1
fi
- name: Build
run: yarn workspaces foreach -R --from "${{ steps.parse.outputs.package_name }}" --topological-dev run build
- name: Test
working-directory: ${{ steps.find.outputs.dir }}
run: |
if jq -e ".scripts.test" package.json > /dev/null; then
yarn test
else
echo "No test script defined"
fi
- name: Publish to npm
working-directory: ${{ steps.find.outputs.dir }}
run: npm publish --access public

monorepo 里最容易出错的是 npm 包设置。每个 workspace 发布到 npm 后,都要在各自的 npm 包页面单独配置 Trusted Publisher。

预发布版本 dist-tag#

如果希望 1.0.0-beta.1 自动发布到 beta tag,可以在发布步骤里根据版本号决定 dist-tag:

- name: Publish to npm
run: |
VERSION=$(jq -r ".version" package.json)
if echo "$VERSION" | grep -qE "(alpha|beta|rc|next)"; then
DIST_TAG=$(echo "$VERSION" | sed -E "s/.*-(alpha|beta|rc|next).*/\1/")
npm publish --access public --tag "$DIST_TAG"
else
npm publish --access public
fi

这样:

1.0.0-beta.1 -> beta
1.0.0-rc.1 -> rc
1.0.0 -> latest

如何测试但不发布#

可以先检查包内容:

Terminal window
npm pack --dry-run

这会展示最终 tarball 会包含哪些文件,但不会发布。

也可以测试发布命令的大部分流程:

Terminal window
npm publish --dry-run --access public

注意:

  • npm publish --dry-run 不会真正发布。
  • 如果当前版本已经发布过,它可能仍然报错:You cannot publish over the previously published versions
  • 这不代表包不能打包,只代表这个版本号已经存在。
  • OIDC / Trusted Publisher 鉴权无法在本地完整验证。
  • 最终鉴权闭环只能通过 GitHub Actions 中的一次真实新版本发布验证。

常见问题#

workflow 报没有权限获取 OIDC token#

检查 workflow 是否包含:

permissions:
id-token: write
contents: read

id-token: write 是 GitHub Actions 生成 OIDC token 的关键权限。

npm 提示 Trusted Publisher 不匹配#

检查 npm 包设置里的这些字段是否完全匹配:

Organization or user
Repository
Workflow filename
Environment name
Allowed actions

尤其注意:

Workflow filename: publish.yml

不要写成:

.github/workflows/publish.yml

如果使用了 GitHub Environment,Environment name 也必须和 workflow 里的环境名一致。

还需要 NPM_TOKEN 吗#

发布公开包通常不需要。

应该删除:

env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

如果项目安装私有依赖,可能仍然需要只读 token 来安装依赖,但发布本身不应该再依赖长期发布 token。

可以继续用 Yarn 或 pnpm 构建吗#

可以。

推荐模式是:

Yarn / pnpm / npm install
Yarn / pnpm / npm build
npm publish

也就是安装和构建沿用项目自己的包管理器,发布统一用 npm CLI。

要不要提交构建产物#

两种方式都可以:

  • 如果 CI 里会构建,就不需要提交 lib / dist
  • 如果 workflow 不构建,就必须确保发布产物已经提交进仓库。

更推荐 CI 中构建,避免本地忘记更新产物。

为什么 npm provenance 没显示#

Trusted Publishing 从 GitHub Actions 或 GitLab CI/CD 发布公开包时,npm 会自动生成 provenance。这个前提通常包括:

  • 使用 Trusted Publishing / OIDC 发布。
  • 仓库是公开仓库。
  • 包是公开包。

如果仓库是私有仓库,即使包本身是公开包,也可能不会生成 provenance。

推荐发布流程#

  1. 修改代码。
  2. 更新 package.json 版本号。
  3. 本地运行构建和测试。
  4. 本地检查包内容:
Terminal window
npm pack --dry-run
  1. 提交代码。
  2. 打 tag:
Terminal window
git tag v1.2.3
git push origin v1.2.3

monorepo 可以使用包名加版本号:

Terminal window
git push origin [email protected]
  1. GitHub Actions 自动发布。
  2. 到 npm 包页面确认新版本已发布。

总结#

Trusted Publishing 的关键不是复杂脚本,而是三件事:

  1. npm 包页面配置 Trusted Publisher。
  2. GitHub Actions workflow 开启 id-token: write
  3. CI 中使用 npm publish 发布。

这样可以去掉长期 npm 发布 token,减少 secret 泄露风险,也让发布来源更清晰。

使用 npm Trusted Publishing 通过 GitHub Actions 自动发布 npm 包
https://lunary.cc/posts/使用-npm-trusted-publishing-通过-github-actions-自动发布-npm-包/
作者
鹤望兰
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0