GitHub Actions NPM可信发布完整教程:从零配置OIDC到成功部署

GitHub Actions NPM可信发布完整教程:从零配置OIDC到成功部署

NPM可信发布完整教程:从零配置到成功部署

NPM可信发布(Trusted Publishing)使用OIDC(OpenID Connect)技术,让 Actions可以直接安全地发布包到NPM,无需管理长期有效的访问令牌。本教程将带你从零开始完成配置。

前置条件

  1. GitHub账号和仓库
  2. NPM账号(需要Pro或付费套餐启用可信发布)
  3. 要发布的Node.js包
  4. 启用GitHub Actions的权限

第一步:NPM账号配置

1.1 启用双因素认证(2FA)

登录NPM官网 → 点击头像 → Settings → Authentication → 启用2FA

1.2 配置发布来源设置

# 在NPM上配置可信发布
 profile enable-2fa auth-and-writes
npm profile set-oidc-issuer "https://token.actions.githubusercontent.com"

第二步:GitHub仓库配置

2.1 添加NPM_TOKEN(作为后备方案)

  1. 访问 https://www.npmjs.com/settings/tokens
  2. 创建新的Access Token,选择"Automation"类型
  3. 复制Token值
  4. 在GitHub仓库:Settings → Secrets and variables → Actions → New repository secret
  5. 名称:NPM_TOKEN,值:粘贴刚才复制的Token

2.2 配置仓库权限

在仓库Settings → Actions → General页面:

  • √ 启用"Read and write permissions"
  • √ 允许GitHub Actions创建和批准拉取请求

第三步:创建GitHub Actions工作流

3.1 基础发布工作流

在项目根目录创建:.github/workflows/publish.yml

name: Publish to NPM

on:
  push:
    tags:
      - 'v*'  # 监听v开头的tag,如v1.0.0
  # 或者使用release触发:
  # release:
  #   types: [published]

jobs:
  publish:
    name: Publish Package to NPM
    runs-on: ubuntu-latest
    
    # 关键:OIDC必需权限
    permissions:
      id-token: write  # 用于OIDC令牌
      contents: read   # 用于代码访问
    
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 获取完整历史用于版本检测
      
      - name: Setup Node.js with OIDC support
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'  # 至少使用Node 20
          registry-url: 'https://registry.npmjs.org/'
          # 注意:Node 18需要额外升级npm步骤
      
      # 验证Node和npm版本(重要!)
      - name: Verify Environment
        run: |
          echo "Node version: $(node --version)"
          echo "npm version: $(npm --version)"
          
          # 确保npm版本支持OIDC
          if ! npm --version | grep -q '^[0-9][0-9]'; then
            echo "⚠️  npm版本可能过低,正在升级..."
            npm install -g npm@latest
          fi
      
      - name: Install Dependencies
        run: npm ci  # 使用clean install确保一致性
      
      - name: Run Tests
        run: npm test  # 发布前运行测试
      
      - name: Build Package
        run: npm run build  # 如果有build脚本
      
      - name: Publish to NPM with OIDC
        run: npm publish --provenance --access public
        env:
          # NPM_TOKEN作为后备方案,OIDC失败时使用
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

3.2 支持旧Node版本的完整工作流

name: Publish to NPM with OIDC

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: Setup Node.js (版本兼容方案)
        uses: actions/setup-node@v4
        with:
          # 如果你的项目需要特定Node版本
          node-version: '18'  # 示例:使用Node 18
          registry-url: 'https://registry.npmjs.org/'
          cache: 'npm'
      
      # 关键:升级npm以支持OIDC
      - name: Upgrade npm for OIDC support
        run: |
          echo "当前npm版本: $(npm --version)"
          
          # 检查是否支持provenance
          if npm config list | grep -q provenance; then
            echo "npm已支持provenance"
          else
            echo "升级npm到最新版本以支持OIDC..."
            npm install -g npm@latest
          fi
          
          echo "升级后npm版本: $(npm --version)"
      
      - name: Install Dependencies
        run: npm ci
      
      # 可选:版本验证
      - name: Validate Package
        run: |
          # 检查package.json
          echo "包名: $(node -p "require('./package.json').name")"
          echo "版本: $(node -p "require('./package.json').version")"
          
          # 验证是否有发布权限
          npm access ls-collaborators $(node -p "require('./package.json').name") || echo "权限检查跳过"
      
      - name: Publish with Provenance
        id: publish
        run: |
          # 获取当前tag版本
          CURRENT_TAG=${GITHUB_REF#refs/tags/}
          PACKAGE_VERSION=$(node -p "require('./package.json').version")
          
          echo "GitHub Tag: $CURRENT_TAG"
          echo "Package Version: $PACKAGE_VERSION"
          
          # 验证版本是否匹配
          if [ "$CURRENT_TAG" != "v$PACKAGE_VERSION" ]; then
            echo "错误:GitHub tag ($CURRENT_TAG) 与package.json版本 (v$PACKAGE_VERSION) 不匹配"
            exit 1
          fi
          
          # 执行发布
          echo "开始发布到NPM..."
          npm publish --provenance --access public
          
          echo "✅ 发布成功!"
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      
      # 发布后操作
      - name: Create GitHub Release
        if: success()
        uses: softprops/action-gh-release@v1
        with:
          generate_release_notes: true
          files: |
            README.md
            CHANGELOG.md
            dist/*.js

第四步:测试配置

4.1 创建测试发布

  1. 更新package.json版本号
  2. 创建并推送git tag:
# 更新版本号
npm version patch  # 或 minor, major

# 推送tag到GitHub
git push --follow-tags

4.2 监控发布过程

  • 访问GitHub仓库的Actions标签页
  • 查看工作流执行日志
  • 检查NPM包的版本发布状态

第五步:高级配置选项

5.1 条件发布(只发布特定分支)

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  publish:
    # 只对tag或main分支的发布事件执行
    if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')

5.2 多环境发布(测试/生产)

name: Publish with Environments

on:
  push:
    tags: ['v*']

jobs:
  test-publish:
    runs-on: ubuntu-latest
    environment: test
    permissions:
      id-token: write
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org/'
      
      - name: Test Publish (Dry Run)
        run: npm publish --dry-run --provenance

  production-publish:
    needs: test-publish
    runs-on: ubuntu-latest
    environment: production
    permissions:
      id-token: write
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org/'
      
      - name: Production Publish
        run: npm publish --provenance --access public

第六步:故障排除

常见错误及解决方案

错误信息 原因 解决方案
npm ERR! code ENEEDAUTH OIDC认证失败 1. 检查permissions包含id-token: write
2. 升级Node到25+或npm到10+
npm notice Access token expired or revoked NPM不支持当前OIDC配置 运行npm install -g npm@latest升级npm
Provenance not enabled NPM账号未启用可信发布 升级到NPM Pro套餐或启用功能
E403 Forbidden 包名已存在或无发布权限 1. 检查包名唯一性
2. 验证NPM账号权限

调试技巧

# 在工作流中添加调试步骤
- name: Debug Information
  run: |
    # 查看环境变量
    echo "GITHUB_REF: $GITHUB_REF"
    echo "GITHUB_SHA: $GITHUB_SHA"
    
    # 检查npm配置
    npm config list
    
    # 测试OIDC令牌
    curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
         "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://registry.npmjs.org" | jq .

第七步:最佳实践

7.1 安全性最佳实践

  • ✅ 始终使用npm ci而不是npm install
  • ✅ 在package.json中固定依赖版本
  • ✅ 启用GitHub Actions的工作流权限限制
  • ✅ 定期轮换NPM_TOKEN(即使主要使用OIDC)
  • ❌ 不要在代码中硬编码任何令牌
  • ❌ 避免使用--always-auth等不安全选项

7.2 性能优化

# 使用缓存加速构建
- name: Cache npm dependencies
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: npm-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      npm-

- name: Cache build outputs
  uses: actions/cache@v3
  with:
    path: dist  # 或你的构建输出目录
    key: build-${{ github.sha }}
    restore-keys: |
      build-

总结

通过GitHub Actions和NPM OIDC可信发布,你可以实现:

  1. 完全自动化的发布流程
  2. 增强安全性,无需管理长期令牌
  3. 可验证的发布来源,提高包的可信度
  4. 版本一致性,确保每次发布都可追溯

记住关键点:

  • 确保使用Node 25+或手动升级npm到10+
  • 工作流中必须包含permissions: id-token: write
  • 使用--provenance标志启用可信发布
  • 始终保留NPM_TOKEN作为后备方案

现在你的NPM包发布流程已经具备了企业级的安全性和可靠性!

进一步学习资源