# 把打了 v* 标签的版本发布到 Gitea 的 npm 包仓库。 # # 触发方式: # 1. 推送版本标签(推荐):git tag -a v1.0.1 -m "..." && git push origin v1.0.1 # 2. 仓库 → Actions → 选择本工作流 → Run workflow(手动,发布当前 ref 的版本) # # 本工作流刻意不使用任何 `uses:` 外部 action:runner 在中国大陆访问 github.com 往往超时 # (表现为所有步骤 cancelled,日志里是 dial tcp ...:443: i/o timeout),而 checkout 只是 # git fetch、Node 本来就在 runner 镜像里。这样工作流只依赖本实例的 git 与镜像自带的 node。 # # 已发布的版本不会让工作流失败:注册表返回 409(version already exists)时这一步记为 # “已存在,本次跳过”,所以手动重跑同一个版本是安全的。 # # 需要的凭据(实测结论,Gitea 1.27.2,本仓库 v1.0.2 那次发布): # 内置任务令牌**不能**发布包。它在注册表眼里是伪用户 gitea-actions(id -2),不是组织成员, # 而 Gitea 的包写权限要求「组织成员且具 admin/write 权限」——于是即使工作流声明了 # permissions: packages: write、组织也把 Actions 令牌权限设成宽松,npm publish 仍返回 # E401 Incorrect or missing password。(组织设置里那张“最大令牌权限”表也没有 packages 这一项。) # 所以发布用 secret NPM_TOKEN = 个人访问令牌,权限勾 write:package;内置令牌只在没配它时兜底。 # # 依赖前提: # - 实例启用了 Actions 并注册了 act_runner;runs-on 的标签要与 runner 一致。 # - runner 镜像里要有 node 与 npm(Gitea 官方 runner-images 自带)。 # - 包名、`cordis.patch.yml` 的 name、客户端模块 id 三处必须一致,改名时别漏。 name: publish on: push: tags: - 'v*' workflow_dispatch: # 读取代码用于检出,向本组织写包。 permissions: contents: read packages: write jobs: npm: runs-on: ubuntu-latest env: REGISTRY: https://gitea.iwake.top/api/packages/dsh-plugin/npm/ steps: - name: Check out the pushed ref run: | set -eu url="${GITHUB_SERVER_URL:-https://gitea.iwake.top}/${GITHUB_REPOSITORY:-dsh-plugin/session-delete}.git" echo "从 ${url} 检出 ${GITHUB_REF}" git init -q . git remote add origin "${url}" git fetch -q --depth 1 origin "${GITHUB_REF}" git checkout -q FETCH_HEAD git log --oneline -1 - name: Show the toolchain run: | set -eu node -v npm -v - name: Read and check the version id: version run: | set -eu version="$(node -p "require('./package.json').version")" name="$(node -p "require('./package.json').name")" case "${GITHUB_REF}" in refs/tags/*) tag="${GITHUB_REF_NAME#v}" if [ "$tag" != "$version" ]; then echo "标签 ${GITHUB_REF_NAME} 与 package.json 的版本 ${version} 不一致" >&2 exit 1 fi ;; esac echo "name=${name}" >> "${GITHUB_OUTPUT}" echo "version=${version}" >> "${GITHUB_OUTPUT}" echo "目标:${name}@${version}" - name: Pack (preview the published contents) run: npm pack --dry-run - name: Publish env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} JOB_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | set -eu # 作用域与默认注册表都指向本组织的 npm 仓库(默认注册表也要设,否则 npm 会拿 # registry.npmjs.org 的 packument 做“是否已发布”判断,甚至可能发错地方)。 # 注意:npm 不允许用 `npm config get` 读回 _authToken(会报 protected),所以不打印它。 npm config set registry "${REGISTRY}" npm config set @dsh-plugin:registry "${REGISTRY}" echo "registry = $(npm config get registry)" RESULT="" LAST_LABEL="" # 结果分类:published 成功 / exists 版本已存在(不视为失败)/ auth 令牌被拒 / error 其他 run_publish() { label="$1" token="$2" LAST_LABEL="${label}" echo "--- 用 ${label} 发布 ---" npm config set "//gitea.iwake.top/api/packages/dsh-plugin/npm/:_authToken" "${token}" set +e output="$(npm publish --registry "${REGISTRY}" --access public 2>&1)" status=$? set -e printf '%s\n' "${output}" if [ "${status}" -eq 0 ]; then RESULT="published"; return 0; fi if printf '%s' "${output}" | grep -Eqi 'E409|already exists|previously published|cannot publish over'; then RESULT="exists"; return 0; fi if printf '%s' "${output}" | grep -Eqi 'E401|E403|EOTP|unauthorized|forbidden'; then RESULT="auth"; return 0; fi RESULT="error" return 1 } if [ -n "${NPM_TOKEN:-}" ]; then run_publish "个人访问令牌 NPM_TOKEN" "${NPM_TOKEN}" elif [ -n "${JOB_TOKEN:-}" ]; then # 兜底路径:本实例实测内置令牌写不了包命名空间(见文件头),这里仍会打印它的身份, # 方便在其它实例上判断到底是"令牌不存在"还是"权限不够"。 server="${GITHUB_SERVER_URL:-https://gitea.iwake.top}" who="$(curl -fsS -H "Authorization: token ${JOB_TOKEN}" "${server}/api/v1/user" 2>/dev/null | head -c 300 || true)" echo "没有配置 NPM_TOKEN,退回内置任务令牌;它的身份是:${who:-(查询失败)}" run_publish "内置任务令牌 GITEA_TOKEN" "${JOB_TOKEN}" else echo "既没有 NPM_TOKEN 也没有内置任务令牌,无法发布" >&2 exit 1 fi case "${RESULT}" in published) echo "已发布 ${{ steps.version.outputs.name }}@${{ steps.version.outputs.version }}(本次用的是 ${LAST_LABEL:-令牌})" ;; exists) echo "${{ steps.version.outputs.name }}@${{ steps.version.outputs.version }} 已存在,本次跳过(不算失败)" ;; *) echo "发布失败" >&2 exit 1 ;; esac