# 发布 dsh-opencode-zen-plugin 到 npmjs 与 CNB 制品库(两个注册表都发,且都是幂等的) # # 触发方式 # 1) 推送版本标签:git tag vX.Y.Z && git push origin vX.Y.Z # 2) 手动触发(Actions → publish → Run workflow);手动触发时,已存在的版本会被跳过 # # npmjs 认证:使用 npm 官方的 Trusted Publishing(OIDC),**不需要任何长期令牌**。 # 配置位置:https://www.npmjs.com/package/dsh-opencode-zen-plugin → Settings → Trusted Publisher # Publisher: GitHub Actions # Organization: A42Null # Repository: dsh-opencode-zen-plugin # Workflow name: publish.yml # Environment: cnb (与本文件的 `environment:` 一致;留空表示不限环境) # # CNB 认证:Basic 认证(_auth = base64("cnb:<访问令牌>")),令牌来自 Environment secret。 # # 一次性前置配置(GitHub 网页;令牌绝不写入本文件) # Settings → Environments → cnb → Environment secrets: # CNB_TOKEN CNB 访问令牌(生成时需勾选「制品库」权限)—— 缺失会让任务失败 # npmjs 不需要任何密钥(若 OIDC 失败,任务会给出配置检查清单并失败) # # 行为要点 # - 包为无作用域名 dsh-opencode-zen-plugin,npmjs 上默认为公开包,无需 --access public。 # - 标签触发时会校验标签版本与 package.json 的 version 一致,避免发错版本。 # - 幂等:目标注册表上已存在同版本则跳过,因此重复推送标签不会产生红色失败。 # - 末尾会校验 npmjs 上确实存在该版本,避免"两步都跳过/都失败"却显示成功。 name: publish on: push: tags: - "v*" workflow_dispatch: permissions: contents: read id-token: write # npm Trusted Publishing(OIDC)需要 concurrency: group: publish-${{ github.ref }} cancel-in-progress: false jobs: publish: name: publish to npmjs and CNB runs-on: ubuntu-latest # 令牌只从名为 cnb 的 Environment secrets 读取 environment: cnb env: CNB_REGISTRY: https://npm.cnb.cool/A42Null/dsh-opencode-zen-plugin/-/packages/ CNB_USERNAME: cnb NPM_REGISTRY: https://registry.npmjs.org steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" - name: Upgrade npm (trusted publishing needs >= 11.5.1) run: | set -euo pipefail npm install -g npm@11 >/dev/null 2>&1 || npm install -g npm@latest echo "npm $(npm --version)" - name: Verify tag matches package.json version if: startsWith(github.ref, 'refs/tags/') run: | tag_version="${GITHUB_REF_NAME#v}" pkg_version="$(node -p "require('./package.json').version")" echo "tag=${tag_version} package.json=${pkg_version}" if [ "$tag_version" != "$pkg_version" ]; then echo "::error::tag ${GITHUB_REF_NAME} does not match package.json version ${pkg_version}" exit 1 fi - name: Syntax check run: | node --check lib/index.js node --check lib/client.js # ---------------- CNB ---------------- - name: Configure CNB registry credentials env: CNB_TOKEN: ${{ secrets.CNB_TOKEN }} run: | set -euo pipefail if [ -z "${CNB_TOKEN:-}" ]; then echo "::error::缺少 Environment secret CNB_TOKEN —— Settings → Environments → cnb → Environment secrets 中添加" exit 1 fi # 去掉可能混入的空白/换行(在网页或命令行写入 secret 时常见) token="$(printf '%s' "$CNB_TOKEN" | tr -d '[:space:]')" if [ -z "$token" ]; then echo "::error::Environment secret CNB_TOKEN 为空或仅含空白字符" exit 1 fi echo "::add-mask::$token" # CNB 用 Basic 认证:_auth = base64("<用户名>:<访问令牌>"),而非 npm 默认的 _authToken auth="$(CNB_TOKEN="$token" node -e "process.stdout.write(Buffer.from(process.env.CNB_USERNAME + ':' + process.env.CNB_TOKEN).toString('base64'))")" echo "::add-mask::$auth" printf '//npm.cnb.cool/A42Null/dsh-opencode-zen-plugin/-/packages/:_auth=%s\n' "$auth" >> "$HOME/.npmrc" chmod 600 "$HOME/.npmrc" echo "已配置 CNB 凭据(令牌来自 Environment secret,未写入仓库)" - name: Verify CNB registry auth run: | set -euo pipefail who="$(npm whoami --registry "$CNB_REGISTRY")" echo "CNB registry user: $who" test "$who" = "$CNB_USERNAME" - name: Publish to CNB (idempotent) run: | set -euo pipefail name="$(node -p "require('./package.json').name")" version="$(node -p "require('./package.json').version")" if npm view "$name@$version" version --registry "$CNB_REGISTRY" >/dev/null 2>&1; then echo "::notice::$name@$version 已存在于 CNB,跳过发布" else npm publish --registry "$CNB_REGISTRY" echo "::notice::已发布 $name@$version 到 CNB" fi # ---------------- npmjs ---------------- - name: Publish to npmjs (trusted publishing / OIDC, idempotent) id: npm_oidc continue-on-error: true run: | set -euo pipefail name="$(node -p "require('./package.json').name")" version="$(node -p "require('./package.json').version")" if npm view "$name@$version" version --registry "$NPM_REGISTRY" >/dev/null 2>&1; then echo "::notice::$name@$version 已存在于 npmjs,跳过发布" exit 0 fi echo "尝试以 Trusted Publishing(OIDC,无令牌)发布..." npm publish --registry "$NPM_REGISTRY" echo "::notice::已通过 Trusted Publishing 发布 $name@$version 到 npmjs" - name: Diagnose npmjs publish failure if: steps.npm_oidc.outcome == 'failure' run: | echo "::error::npmjs 发布失败(OIDC 可信发布)—— 请检查:" echo "::error::1) npmjs 包设置里已配置 Trusted Publisher:Publisher=GitHub Actions, Organization=A42Null, Repository=dsh-opencode-zen-plugin, Workflow name=publish.yml, Environment=cnb" echo "::error::2) 本工作流声明了 permissions: id-token: write" echo "::error::3) runner 上的 npm >= 11.5.1(本工作流会先升级 npm)" exit 1 - name: Verify npmjs has this version if: always() run: | set -euo pipefail name="$(node -p "require('./package.json').name")" version="$(node -p "require('./package.json').version")" # 刚发布的版本在 npm 上可能处于 "being processed" 状态,需要给它一点时间 for attempt in $(seq 1 24); do if npm view "$name@$version" version --registry "$NPM_REGISTRY" >/dev/null 2>&1; then echo "::notice::npmjs 上已存在 $name@$version(第 ${attempt} 次检查)" exit 0 fi echo "第 ${attempt} 次检查尚不可见,10 秒后重试..." sleep 10 done echo "::error::npmjs 上仍不存在 $name@$version —— 发布未成功" exit 1 - name: Summary if: always() run: | name="$(node -p "require('./package.json').name")" version="$(node -p "require('./package.json').version")" { echo "### $name@$version" echo "" echo "- npmjs: https://www.npmjs.com/package/$name" echo "- CNB: $CNB_REGISTRY" } >> "$GITHUB_STEP_SUMMARY"