【免费下载链接】gemini-watermark-remover
A high-performance, 100% client-side tool for removing Gemini AI image & video watermarks. Built with pure JavaScript using mathematically precise Reverse Alpha Blending. / 基于 JavaScript 的纯浏览器端 Gemini AI 图像和视频无损去水印工具,使用数学精确的反向 Alpha 混合算法
本文以@pilio/gemini-watermark-remover开源仓库为实例,讲解如何通过 npmjs.com 的Trusted Publisher能力,让 GitHub Actions 利用 OIDC(OpenID Connect)身份直接发布 npm 包,从而彻底告别长期NPM_TOKEN。读完本文,你将掌握 Trusted Publisher 的一次性配置步骤、publish-npm.yml工作流的源码级校验逻辑、手动重跑发布的全部前置条件,以及将发布访问升级为「2FA + 禁用 Token」的安全加固流程,可直接迁移到自己的 npm 仓库。
为什么需要 Trusted Publishing:不保存长期NPM_TOKEN
传统做法是在 GitHub Secrets 中保存一个长期有效的 npm automation token(NPM_TOKEN),工作流运行时再把它注入npm publish。这种做法有两个明显的短板:
- 凭证泄露面大:长期 token 一旦被 fork、被恶意 PR 或日志泄露,攻击者可以长期冒用发布身份;
- 缺少发布来源证明:npm 无法自动证明「这个包是由仓库 A 的提交 B 构建发布的」。
Trusted Publishing 的思路与此相反:npmjs.com 信任 GitHub 的 OIDC 身份提供方,GitHub Actions 在每次运行时通过id-token: write权限动态申请一个短期、绑定本次 workflow run 的身份令牌,npm 据此完成认证,仓库内不再保存任何长期NPM_TOKEN。由于每次发布的身份都是临时签发的,即使凭证泄露也会在运行结束后自然失效;同时 npm 会自动为该次发布生成provenance(发布来源证明),将包与具体的仓库、工作流和提交绑定。
在@pilio/gemini-watermark-remover仓库中,这一方案已经落地:发布工作流 .github/workflows/publish-npm.yml 的顶层permissions只声明了两个权限,见该文件 L9-L11:
permissions: contents: read id-token: writecontents: read只允许读取仓库内容,id-token: write专用于申请 OIDC 令牌,其余权限一律未授予——这是整个发布链路能够不依赖NPM_TOKEN的基石。
npmjs.com 一次性配置:将 GitHub Actions 注册为 Trusted Publisher
该步骤只需要在 npmjs.com 上配置一次,之后所有版本都由工作流自动发布。具体操作如下:
- 登录 npmjs.com,打开
@pilio/gemini-watermark-remover的package settings; - 在Trusted Publisher区域选择GitHub Actions;
- 按下表填写各项:
| 配置项 | 填写值 | 说明 |
|---|---|---|
| Organization or user | GargantuaX | 拥有目标仓库的 GitHub 组织或用户名,与本仓库 package.json 中author字段及repository归属一致 |
| Repository | gemini-watermark-remover | 发布来源仓库名 |
| Workflow filename | publish-npm.yml | 必须与仓库中 .github/workflows/publish-npm.yml 的文件名完全一致,npm 只信任该工作流签发的 OIDC 身份 |
| Environment name | 留空 | 该仓库未使用 GitHub Actions 的 deployment environment,因此留空;如果工作流被限制在某个 environment 中,此处需填写对应的 environment 名 |
| Allowed actions | 只选择npm publish | 只允许npm publish这一个 action,最大程度缩小可执行操作面 |
保存后,先完成一次 OIDC 发布并确认成功,再把Publishing access改为Require two-factor authentication and disallow tokens,最后撤销所有不再使用的 npm automation token(具体见下文「安全加固」小节)。
发布工作流源码解析:从 Release 到 npm Registry
配置好 Trusted Publisher 后,日常发布完全由工作流自动完成。打开 .github/workflows/publish-npm.yml 可以看到它的完整结构。
触发方式:Release 发布 + 手动触发
on: release: types: - published workflow_dispatch:- 当维护者创建并发布一个 GitHub Release(
release: published)时,工作流自动运行; workflow_dispatch允许在 GitHub Actions 界面手动运行Publish npm package,用于 GitHub Release 已经存在、但需要重新发布 npm 包的场景(例如首次迁移 Trusted Publishing 时补齐发布记录)。
并发控制与运行环境
concurrency: group: npm-publish cancel-in-progress: false jobs: publish: runs-on: ubuntu-latest发布使用 GitHub-hosted runner(ubuntu-latest),并通过concurrency.group: npm-publish保证同一时间只允许一个发布任务运行;cancel-in-progress: false意味着后来的任务会排队等待而非取消正在进行的发布,避免并发发布互相覆盖导致 registry 状态错乱。
环境准备三步
- name: Checkout release source uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup pnpm uses: pnpm/action-setup@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org package-manager-cache: false关键点在于actions/checkout@v6的fetch-depth: 0:发布校验需要访问 git tag 指向的历史对象(计算 tag 中 tarball 的 SHA-256、判断 tag 是否为当前提交祖先),因此必须拉取完整历史而非浅克隆。Node.js 版本固定为 24,与 package.json 中packageManager: pnpm@10.11.0声明的 pnpm 版本配合使用;registry-url指向 npm 官方 registry,Trusted Publishing 的 OIDC 认证正是通过 setup-node 写入的 npmrc 生效。
核心校验步骤:发布前逐条验证版本化.tgz
工作流中名为Validate versioned release package(L37-L80)的步骤是整个发布流程的灵魂。它以set -euo pipefail严格模式执行以下校验链:
package_name="$(node --input-type=module -e "import manifest from './package.json' with { type: 'json' }; process.stdout.write(manifest.name)")" version="$(node --input-type=module -e "import manifest from './package.json' with { type: 'json' }; process.stdout.write(manifest.version)")" tag="v${version}" tarball="release/pilio-gemini-watermark-remover-${version}.tgz"先读取当前package.json中的包名与版本,推导出预期的 git tag(v<version>)与 tarball 路径(release/pilio-gemini-watermark-remover-<version>.tgz),然后依次执行五重校验:
- Release tag 与包版本一致:如果本次由 Release 事件触发,
github.event.release.tag_name必须等于v${version},否则直接失败; v<version>标签存在且是当前提交的祖先:通过git rev-parse --verify refs/tags/${tag}^{commit}确认标签对象存在,再用git merge-base --is-ancestor "${tag}" HEAD确认该标签在提交历史中早于或等于当前 checkout 的提交——这保证发布的正是被标签锚定的那份代码;- 版本化
.tgz已提交:test -f "${tarball}"确认release/下确实存在与版本对应的 tarball 文件; - 当前
.tgz与该标签中的文件 SHA-256 完全一致:
tag_tarball_sha="$(git show "${tag}:${tarball}" | sha256sum | cut -d ' ' -f 1)" checkout_tarball_sha="$(sha256sum "${tarball}" | cut -d ' ' -f 1)" if [[ "${tag_tarball_sha}" != "${checkout_tarball_sha}" ]]; then echo "The checked-out tarball differs from the artifact committed at ${tag}." >&2 exit 1 fi用git show读出标签指向提交中 tarball 的字节流并计算 SHA-256,与工作区实际文件的 SHA-256 比对。这保证了「发布的内容 = 打标签时审阅过的内容」,杜绝了标签之后对release/*.tgz的篡改被静默发布; 5..tgz内的包名和版本与仓库package.json一致:用tar -xOf直接解出 tarball 内部的package/package.json,在一个内联 Node 脚本中校验packed.name与packed.version是否分别等于仓库package.json的name与version,防止「仓库版本号已更新但 tarball 还是旧包」的错配发布。
校验通过后,步骤把package_name、version、tag、tarball写入GITHUB_OUTPUT,供后续步骤消费。
幂等发布:版本已存在时安全跳过
发布前还有一个专门步骤Check whether version is already published(L82-L96):
if published_version="$(pnpm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version 2>/dev/null)" && [[ "${published_version}" == "${PACKAGE_VERSION}" ]]; then echo "already_published=true" >> "${GITHUB_OUTPUT}" echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already published; nothing to do." else echo "already_published=false" >> "${GITHUB_OUTPUT}" fi它通过pnpm view查询 npm registry,若目标版本已经存在则标记already_published=true,真正的发布步骤会跳过:
- name: Publish with npm Trusted Publishing if: steps.registry.outputs.already_published != 'true' run: pnpm publish "./${{ steps.release.outputs.tarball }}" --access public --no-git-checks最终发布命令是对版本化 tarball 执行pnpm publish:
--access public:以公开 scope 发布(@pilio为公开包,见 package.json);--no-git-checks:跳过 pnpm 默认的 git 状态检查,因为发布源是已经过严格校验的标签化 tarball,而不是当前工作树;- 没有注入任何
NPM_TOKEN:认证完全由 setup-node 配置的 registry + OIDC 身份完成,npm 会为本次发布自动生成 provenance。
注意--no-git-checks并不意味着降低安全标准——恰恰相反,它只有在前面「五重校验」全部通过的前提下才会被执行。仓库测试 tests/project/publishNpmWorkflow.test.js 专门验证了这一点:该测试构造一个 detached tag checkout(模拟发布现场),用正则从工作流中提取真实的 publish 命令,并对本地 pack 出的 tarball 执行pnpm publish --dry-run,确保「从标签化 tarball 发布」这条命令路径在任何 git 状态(包括 detached HEAD)下都能正确工作。
手动重跑发布:GitHub Release 已存在时的workflow_dispatch
如果 GitHub Release 已经存在(例如仓库从旧的NPM_TOKEN方案首次迁移到 Trusted Publishing 时,需要补发当前版本),可以在 GitHub Actions 页面手动运行Publish npm package。手动任务同样执行上面完整的五重校验,因此必须同时满足:
v<version>标签存在,且是当前提交的祖先;- 版本化
.tgz(release/pilio-gemini-watermark-remover-<version>.tgz)已提交到仓库; - 当前 checkout 的
.tgz与该标签中的文件 SHA-256 完全一致; .tgz内的包名和版本与仓库package.json一致。
其中「标签是当前提交祖先」这一条在手动触发时尤为重要:如果直接对某个没有 tag 锚定的最新提交手动发布,第二步校验会失败,从而防止发布未经 Release 流程审阅的内容。由于发布是幂等的,即使该版本已经存在于 npm,任务也会安全跳过,不会产生错误覆盖。
安全加固:发布后收紧访问与撤销 Token
Trusted Publishing 配置完成并通过一次实际发布验证后,应立即执行以下加固(原文档「保存后先完成一次 OIDC 发布并确认成功,再把 Publishing access 改为 Require two-factor authentication and disallow tokens,最后撤销不再使用的 npm automation token」):
- 完成一次 OIDC 发布并确认成功:先让 Trusted Publisher 链路实际跑通一次,避免尚未验证配置就锁死发布通道;
- 将 Publishing access 改为
Require two-factor authentication and disallow tokens:此后 npm 账号下所有长期 token 一律失效,只有通过 OIDC 的 Trusted Publishing 路径可以发布,把攻击面收敛到「只有publish-npm.yml这个被 npm 明确信任的工作流」; - 撤销不再使用的 npm automation token:包括此前用于旧发布流程的
NPM_TOKEN,以及任何其他遗留 automation token,确保仓库 Secrets 中不再存在可长期使用的发布凭证。
结合工作流 L9-L11 的最小权限声明,这套方案形成三层防护:临时 OIDC 身份(分钟级有效期)、仅允许npm publish的 Trusted Publisher 配置、账号级禁用 token + 强制 2FA。
发布后的验证:release:distribution-check与 npm 面确认
发布不是终点。仓库通过 scripts/check-release-distribution.js 提供发布分布检查(对应pnpm release:distribution-check),它会同时核对多个发布面,其中与 npm 相关的是:
- npm registry 检查:请求
https://registry.npmjs.org/@pilio%2Fgemini-watermark-remover,取dist-tags.latest,与package.json中的期望版本比对(对应源码中npmUrl与npmLatest的checkVersion('npm-latest', ...)逻辑); - GitHub Release 资产检查:期望
v<version>的 release assets 中必须包含pilio-gemini-watermark-remover-<version>.tgz,与发布工作流消费的 tarball 命名规则完全一致; - 其余检查项还包括官网 userscript、Chrome 扩展 fallback 包与 SHA-256、
latest-extension.json等发布面。
RELEASE.md 的 Post-Release 清单也明确要求:确认pnpm release:distribution-check对 local package、GitHub Release、release assets、npm latest 等面均输出ok,并以生成的报告作为发布面状态的最终依据。这些是检验「OIDC 发布是否真正生效」的客观证据。
最佳实践小结
回顾整个@pilio/gemini-watermark-remover的 Trusted Publishing 落地,可以提炼出几条可直接复用的经验:
- 工作流文件名即信任边界:npmjs.com 上的
Workflow filename必须与仓库中的 .github/workflows/publish-npm.yml 完全一致,改名或迁移仓库后需要在 npm 侧同步更新; - 校验交给工作流,而不是人的自觉:版本对齐、标签祖先、tarball SHA-256、包内 manifest 一致性这四类校验全部脚本化,发布前任何一环失败都会让任务整体失败,配合 tests/project/publishNpmWorkflow.test.js 的回归测试,保证校验逻辑本身不被意外破坏;
- 发布产物先于发布存在:版本化
.tgz必须预先构建并提交到release/目录,publish-npm.yml只是对它做验证与上传,这与 .github/workflows/ci.yml 中先pnpm pack打包 release candidate、再由发布流程消费的思路一致; - 权限最小化 + 账号级禁用 token:工作流只保留
contents: read与id-token: write,npm 侧再叠加「仅允许npm publish」与「2FA + disallow tokens」,让 OIDC 短期凭证成为唯一的发布钥匙; - 发布后立即验证:用
pnpm release:distribution-check核对 npmdist-tags.latest与 GitHub Release 资产,把「发布成功」落实为可审计的证据,而不是只看 CI 任务绿了。
这套配置同样适用于任何托管在 GitHub、以 npm 包形式分发的开源项目:只需在 npmjs.com 注册 Trusted Publisher、提交一份带五重校验的publish-npm.yml,即可在不保存任何长期 token 的前提下,获得自动 provenance 的合规发布链路。
【免费下载链接】gemini-watermark-remover
A high-performance, 100% client-side tool for removing Gemini AI image & video watermarks. Built with pure JavaScript using mathematically precise Reverse Alpha Blending. / 基于 JavaScript 的纯浏览器端 Gemini AI 图像和视频无损去水印工具,使用数学精确的反向 Alpha 混合算法
相关推荐
Etherpad npm Trusted Publishing(OIDC)实战指南:告别 NPM_TOKEN,用 GitHub Actions OIDC 安全发布插件
Etherpad npm Trusted Publishing(OIDC)实战指南:告别 NPM_TOKEN,用 GitHub Actions OIDC 安全发
后端协同办公WebSocket前端富文本NuGet Trusted Publishing 发布工作流模板:用 GitHub Actions OIDC 实现无 API Key 的包发布
NuGet Trusted Publishing 发布工作流模板:用 GitHub Actions OIDC 实现无 API Key 的包发布 导读 本文以本仓
人工智能AI 技能AI 评测Benchmark开发工具agent-governance-toolkit 发布实战:为 ATR 工具注册表配置 PyPI Trusted Publishing(OIDC)自动化发布
agent governance toolkit 发布实战:为 ATR 工具注册表配置 PyPI Trusted Publishing(OIDC)自动化发布 本
人工智能AI AgentAI 安全治理策略引擎认证鉴权Agent 沙箱可观测性
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考