ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

BilibiliSponsorBlock 发版全流程实战:从版本号 bump、GitHub Release 到 Chrome/Firefox/Edge 商店提审

BilibiliSponsorBlock 发版全流程实战:从版本号 bump、GitHub Release 到 Chrome/Firefox/Edge 商店提审
  • 前端

【免费下载链接】BilibiliSponsorBlock

一款跳过小电视视频中恰饭片段的浏览器插件,移植自 SponsorBlock。A browser extension to skip sponsored segments in videos, ported from the SponsorBlock

项目地址:https://gitcode.com/gh_mirrors/bi/BilibiliSponsorBlock
点击查看免费下载

本篇技术指南面向需要为 BilibiliSponsorBlock(小电视空降助手)执行版本发布、商店提审或发布状态核查的开发者与 CI 维护者。全文以仓库.agents/skills/browser-extension-release技能文档为核心骨架,结合 .github/workflows 下的真实工作流与 manifest/manifest.json 等配置展开,覆盖发版前盘点、版本号 bump、本地与 CI 验证、Release 文案规范、商店凭据管理、we pub 提交与失败排查的完整链路。读完本文,你将能按仓库惯例安全地发布一个新版本,并能准确区分"上传成功 / 已提交审核 / 审核通过 / 公开上线"这四档状态。

一、发版技能文档的定位与核心约束

.agents/skills/browser-extension-release/SKILL.md是仓库内置的 Agent 发版技能,声明了明确的适用范围:只针对当前BilibiliSponsorBlock仓库本身的版本发布,以仓库当前配置和 Git 历史为准,不把服务端仓库的版本或流程混进来。技能按任务粒度拆分为两份参考文档:

  • references/release-process.md:负责版本号、commit、tag、GitHub Release、Release 文案与附件;
  • references/store-publishing.md:负责商店凭据、Chrome/Firefox/Edge 提交、审核状态与失败排查。

完整发版必须依次读取两份 reference;如果只做状态查询,则只需读取对应的一份。

该技能定义了七条"不可破坏的约束",是发版操作的安全边界:

  1. 先确认工作仓库是BilibiliSponsorBlock,读取 AGENTS.md,检查git status、远端分支和最新 tag;
  2. 版本号唯一来源是 manifest/manifest.json,历史上不随扩展版本修改 package.json 的version(当前仓库中package.json的 version 仍停留在0.1.10,而 manifest 已是0.16.0,恰好印证了这一约定);
  3. 保留用户已有改动;远端前进时先 fetch 再 rebase,禁止 force push 和覆盖远端提交;
  4. 构建、查询和校验不代表允许发布——执行git push、创建 tag/Release、上传商店或重新提交商店前,必须确认用户已授权对应外部变更;
  5. 不读取、打印或要求用户在聊天中发送 Secret 值,只检查 GitHub Secret 名称与更新时间;Actions 日志中若意外出现未遮罩的敏感值,应停止展示日志并提醒轮换;
  6. 商店发布可能部分成功,重试时只开启失败的商店,绝不为让总 workflow 变绿而重复提交已成功的商店;
  7. 正式发布必须从固定版本 tag运行,而不是从会继续变化的master运行;只做构建验证才可以从master运行并关闭全部商店开关。

二、发版前盘点:只读检查与变更核实

发版的第一步是在仓库根目录执行只读盘点,技能文档给出了完整命令组:

git status --short --branch git fetch origin master --tags git log --oneline --decorate -20 git tag --sort=-version:refname | head -15 gh api 'repos/hanydd/BilibiliSponsorBlock/releases?per_page=10' \ --jq '.[] | {tag_name,name,published_at,html_url,assets:[.assets[].name]}'

要点:

  • 版本冲突检查:确认目标版本高于 GitHub Release 和三个商店的已有版本。商店不接受重复或降低的版本号,这是发布工作流的一条硬性前提(见 docs/publishing.md)。
  • 版本决策:若用户未指定版本,可根据变更兼容性建议 patch/minor/major,但在动手修改前必须让用户确认。
  • 真实变更核查:不要只依据 commit 标题判断变更内容:
git log PREVIOUS_TAG..HEAD --reverse --format='%h%x09%an%x09%s' git diff --stat PREVIOUS_TAG..HEAD

对标题模糊的重构或修复,应进一步查看实际 diff 后再写进 Release 文案。

三、版本号 bump 与版本 commit

仓库的历史约定是只修改manifest/manifest.json一个文件,把version字段改为目标版本,不修改package.json。当前 manifest/manifest.json 的版本为0.16.0,其中的name、short_name、description均使用__MSG_*__国际化占位符,真正的文案在 public/_locales 中按语言分发。

版本提交信息固定为:

Bump Version X.Y.Z

提交命令:

git add manifest/manifest.json git commit -m 'Bump Version X.Y.Z'

关键惯例:Release tag 应直接指向这个 bump commit。为保持这种关系,通常先完成普通代码、CI 和文档变更,最后再做 bump commit 和 tag。需要创建 Release 时记录其 SHA:

version='X.Y.Z' bump_commit="$(git rev-parse HEAD)"

提交前运行git diff --check检查空白错误。若远端master已前进,fetch 后审查新增提交并 rebase;解决冲突时既要保留远端的新行为,也要保留目标版本号。

从构建插件 webpack/webpack.manifest.js 的实现看,manifest.json是所有浏览器构建的公共底座:构建时会读取manifest/manifest.json,再按chrome/firefox/safari分别与 manifest/chrome-manifest-extra.json、manifest/firefox-manifest-extra.json、manifest/safari-manifest-extra.json 做深度合并后写入dist/manifest.json。这也解释了为什么版本号只落在manifest/manifest.json一处,就能同时控制三个商店包与 Safari 构建的版本。

四、本地与 GitHub CI 验证

发布前至少运行以下本地验证(仅当config.json不存在时才拷贝,且按 AGENTS.md 的提醒,config.json必须存在才能构建):

cp config.json.example config.json # 仅当 config.json 不存在 npm ci npm run lint npm run test
  • npm run lint执行eslint src;
  • npm run test会先执行一次完整的 Chrome 构建再跑 Jest(跳过构建可用npm run test-without-building),这正是 package.json 中"test": "npm run build:chrome && npx jest"的定义。

推送后等待仓库原有的两个工作流:

  • .github/workflows/ci.yml(CI):执行npx ts-node scripts/validate-ota.ts校验 OTA 配置、lint,并构建 Chrome/Firefox/Safari 三套产物;
  • .github/workflows/tests.yml(Tests):运行 Jest 单测,并安装 Playwright Chromium 后执行npm run test:e2e集成测试。

不要在CI或Tests失败时创建 Release,除非用户明确接受并说明原因。

需要验证发布流水线本身、但不触碰商店时,可从master触发商店工作流并关闭全部商店开关:

gh workflow run publish-stores.yml --ref master \ -f chrome=false -f firefox=false -f edge=false

然后等待Test and packagejob 完成,确认三种 ZIP 版本一致且 artifact 上传成功。

五、Release 文案风格与创建

5.1 文案风格

先查看最近几个 Release 的正文作为风格参照:

for tag in 0.13.1 0.13.0 0.12.1; do gh api "repos/hanydd/BilibiliSponsorBlock/releases/tags/$tag" \ --jq '{tag_name,body}' done

本仓库使用简短中文无序列表:

- 修复……的问题 - 添加……功能 #123 @contributor - 优化……

写作规则要点:

  • 不加"更新内容""What's Changed"等标题;不保留只有Full Changelog的自动生成正文;
  • 每条描述一个用户能理解的修复、功能、兼容性变化、重要测试能力或发布能力;合并相近的内部提交,不逐条复制 commit log;
  • 破坏性变化、移除的功能和默认行为变化必须明确写出;
  • 有外部贡献者时沿用历史格式标记@username,issue/PR 编号确有帮助时再添加;
  • 不夸大"上传成功"为"已上线"。

创建 Release 前用临时 notes 文件复查文字和版本范围。

5.2 创建 Release

先推送版本 commit 并等待 CI 通过,再创建同名 Release:

version='X.Y.Z' bump_commit="$(git rev-parse HEAD)" git push origin master gh release create "$version" \ --target "$bump_commit" \ --title "$version" \ --notes-file /path/to/release-notes.md

注意gh release create会在 tag 不存在时自动创建同名 tag,因此创建前必须确认 tag 和 Release 均不存在;不要移动已公开的 tag。创建后可核对 tag 与 bump commit 的 SHA 是否一致:

git fetch origin "tag" "$version" tag_commit="$(git rev-parse "$version^{commit}") echo "bump=$bump_commit tag=$tag_commit"

5.3 Release 附件由工作流自动上传

发布 Release 会触发 .github/workflows/release.yml 的Upload Release Buildjob,该 workflow 依次构建并上传四个附件:

ChromeExtension.zip FirefoxExtension.zip EdgeExtension.zip SafariExtension.zip

从源码看,buildjob 使用 Node 20,先cp config.json.example config.json、npm ci,然后按顺序执行npm run build:chrome/build:firefox/build:edge/build:safari(每次换浏览器前rm -rf ./dist清理),用zip -r打包dist目录后通过Shopify/upload-to-release@v2.0.0上传;另有独立的safari-macosjob 通过 .github/workflows/safari-macos.yml 在 macOS runner 上构建 Safari 原生 app 并上传。

等待附件上传:

gh run list --workflow release.yml --limit 3 gh run watch RUN_ID --exit-status

最后通过 Releases API 核对正文、tag、目标 commit、附件名称和大小。若 Release 已创建但文案需要修正,使用gh release edit X.Y.Z --notes-file ...;修改正文不会重新触发附件构建。

六、商店提交:publish-stores 工作流与凭据环境

6.1 发布入口与触发方式

商店发布入口是 .github/workflows/publish-stores.yml(Publish extension stores),它构建并校验 Chrome、Firefox、Edge 三个包,然后用wepub调用各商店 API。工作流只支持更新已有商店项目,不创建新的商店条目。

正式发布必须从不可变 tag 运行:

gh workflow run publish-stores.yml --ref X.Y.Z \ -f chrome=true -f firefox=true -f edge=true

三个布尔输入默认全为true。若某个商店已成功,重试时把它设为false,例如只重试 Edge:

gh workflow run publish-stores.yml --ref X.Y.Z \ -f chrome=false -f firefox=false -f edge=true

从 workflow 定义看,buildjob(Test and package)包含完整的流水线:npm ci、拷贝配置、读取 manifest 版本、lint、test、分别用npm run build:{chrome,firefox,edge}+npx web-ext build打包三个 ZIP(Firefox 额外用git archive生成源码归档firefox-source.zip),随后校验三个包内manifest.json的 version 是否与仓库 manifest 一致,生成certification-notes.txt(版本 + source commit + 最新提交说明)后以extension-store-packages-<version>为名上传 artifact,保留 14 天。三个发布 job(chrome/firefox/edge)并行运行,均使用browser-storesGitHub Environment。

6.2 GitHub Environment 配置

首次配置时,在仓库的Settings > Environments中创建browser-stores环境(对应文档 docs/publishing.md 的首节)。

Variables(普通标识,公开可见):

Variable用途
CHROME_PUBLISHER_IDChrome Web Store Developer Dashboard 的 Publisher > Settings 中的 Publisher ID
CHROME_ITEM_IDChrome 扩展 ID;未配置时 workflow 使用默认值eaoelafamejbnggahofapllmfhlhajdd(publish-stores.yml)
EDGE_PRODUCT_IDPartner Center 中 Edge extension overview 的 Product ID
FIREFOX_ADDON_IDAMO Add-on ID;未配置时 workflow 使用默认值{f10c197e-c2a4-43b6-a982-7e186f7c63d9}(publish-stores.yml)

Secrets(凭据,必须用 GitHub 加密存储):

Secret获取位置
CHROME_CLIENT_IDGoogle Cloud 中启用 Chrome Web Store API 后创建的 Web application OAuth client
CHROME_CLIENT_SECRET同上 OAuth client 的 secret
CHROME_REFRESH_TOKEN使用 Chrome Web Store scope 生成的 OAuth refresh token(不是 access token)
FIREFOX_API_KEYAMO API Credentials 页面中的 JWT issuer
FIREFOX_API_SECRETAMO API Credentials 页面中的 JWT secret
EDGE_CLIENT_IDPartner Center 的 Microsoft Edge > Publish API 页面
EDGE_API_KEYPartner Center 的 Publish API 页面

凭据检查只用名称与更新时间(不输出值):

gh api repos/hanydd/BilibiliSponsorBlock/environments/browser-stores/variables \ --jq '.variables[] | {name,updated_at}' gh api repos/hanydd/BilibiliSponsorBlock/environments/browser-stores/secrets \ --jq '.secrets[] | {name,updated_at}'

不要把凭据写进仓库文件、Issue、Actions 日志或聊天记录。同时注意:GitHub 会遮罩 Secrets,但Variables 会明文显示在日志中,因此 Variables 不得存放任何敏感值。

6.3 Chrome 凭据的生成要点

Chrome 使用 OAuth 访问 Chrome Web Store API,凭据细节来自 references/store-publishing.md:

  • CHROME_PUBLISHER_ID:来自 Chrome Web Store Developer Dashboard 的Publisher > Settings;
  • CHROME_CLIENT_ID/CHROME_CLIENT_SECRET:Google Cloud 中启用 Chrome Web Store API 后创建的Web applicationOAuth client;
  • Authorized redirect URI 必须精确为https://developers.google.com/oauthplayground,放在redirect URIs(不是 JavaScript origins),且末尾没有/;
  • OAuth Playground 使用 scopehttps://www.googleapis.com/auth/chromewebstore并勾选 offline access,保存响应中的refresh_token,不是一小时过期的access_token;
  • OAuth 应用应为In production(管理自己的 Chrome 扩展不需要申请 Google 验证);Testing 状态签发的 refresh token 通常 7 天过期,切换正式版后需重新完整授权并替换CHROME_REFRESH_TOKEN;
  • 常见错误:redirect_uri_mismatch表示 redirect URI 或 Client ID 不匹配;403 access_denied且提示 testing,表示登录账号不在 Test users,或应用仍未切换正式版。

6.4 Chrome 凭据的定期自检

仓库提供了独立工作流 .github/workflows/check-chrome-credentials.yml(Check Chrome credentials),每周一北京时间 09:23(cron23 1 * * 1,UTC+8)定时运行,也可手动触发。它复用browser-stores中的 Chrome 凭据,先用 refresh token 换取临时 access token,再调用只读的fetchStatus查询当前扩展;不构建、不上传、不发布,也不修改 GitHub Secrets。

其底层逻辑在 scripts/check-chrome-credentials.mjs 中:

  • 校验CHROME_PUBLISHER_ID、CHROME_ITEM_ID、CHROME_CLIENT_ID、CHROME_CLIENT_SECRET、CHROME_REFRESH_TOKEN五项是否齐全;
  • POST 到https://oauth2.googleapis.com/token(grant_type: refresh_token)换取 access token;
  • 用 Bearer token 请求https://chromewebstore.googleapis.com/v2/publishers/{id}/items/{itemId}:fetchStatus,并校验响应中的itemId与配置一致;
  • 出错时绝不输出响应体(响应体可能含凭据),只给出针对性建议:invalid_grant→ 重新授权并替换CHROME_REFRESH_TOKEN;invalid_client→ 检查 Client ID / Client Secret;401/403 → 检查 OAuth scope、开发者账号访问与 publisher/item ID;429/5xx → 服务限流或不可用,稍后重试。

该脚本的本地测试不需要真实凭据:

node --test scripts/check-chrome-credentials.test.mjs

注意两个限制(见 docs/publishing.md):定时检查不能保证 refresh token 永不失效;公开仓库连续 60 天没有活动时 GitHub 会自动停用定时工作流,需要到 Actions 页面重新启用。

6.5 Firefox 与 Edge 凭据要点

  • Firefox:在https://addons.mozilla.org/developers/addon/api/key/获取 JWT issuer(→FIREFOX_API_KEY)与 JWT secret(→FIREFOX_API_SECRET)。workflow 以listed渠道创建版本,并上传可构建的源码归档;不要把unlisted签名误当商店更新。对应命令为:
wepub firefox artifacts/firefox-extension.zip \ --channel listed \ --compatibility firefox,android \ --source artifacts/firefox-source.zip
  • Edge:Partner Center 的Microsoft Edge > Publish API使用 v1.1 新体验,Client ID →EDGE_CLIENT_ID,API key →EDGE_API_KEY。EDGE_PRODUCT_ID必须是 Partner Center GUID(形如xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),不要填 32 位 Chromium Extension ID;填错时上传端点通常返回 404。API key 有过期日期,需要轮换。

七、运行与日志检查

触发商店工作流后记录 run ID 并等待:

gh run list --workflow publish-stores.yml --limit 3 gh run watch RUN_ID --exit-status

即使总 workflow 失败,也要分别检查每个 job——其他商店可能已经成功:

gh api repos/hanydd/BilibiliSponsorBlock/actions/runs/RUN_ID/jobs \ --jq '.jobs[] | {id,name,status,conclusion}' gh run view RUN_ID --job JOB_ID --log

只展示与结论有关的日志片段,避免在输出中保留可能泄露敏感信息的完整日志。

八、成功判据:四档状态严格区分

技能文档要求最终报告必须分别列出 GitHub Release、Chrome、Firefox 和 Edge 的状态,并区分"上传成功 / 已提交审核 / 审核通过 / 公开上线"。

Chrome

日志应依次出现:

the access token fetched the package archive uploaded, upload_state="SUCCEEDED" the draft submitted, item_state="PENDING_REVIEW"

PENDING_REVIEW表示已提交审核,不是已上线;公开商店仍显示旧版本属于正常现象。不要用相同版本重复上传来测试新的 refresh token。

Firefox

日志应出现 new version created 和 source archive uploaded。可用公开 API 验证:

https://addons.mozilla.org/api/v5/addons/addon/bilisponsorblock/

只有当current_version为目标版本且status为public时,才能报告"公开上线"。

Edge

日志应出现 package archive uploaded、upload processed、draft submitted、submission processed。最后一项表示已交给 Edge 认证流程,不代表商店已公开。

九、常见失败与停止条件

技能文档给出了一套"先停后查、按状态修复"的排查表:

现象处理
缺少变量/Secret停止在凭据检查,不调用商店;让用户在 GitHub Environment 中补齐
Chrome 401 /invalid_grantrefresh token 无效、过期或与 Client 不匹配;重新授权并只替换 refresh token
Chrome 已有相同版本或正在审核不要重复发布;查询后台状态
Firefoxversion already exists不要删除已创建版本;确认第一次提交是否已成功
Edge 404先检查 Product ID 是否为 Partner Center GUID
Edge 401/403检查 Publish API v1.1 的 Client ID、API key 与过期日期
任一商店已成功、另一个失败仅重跑失败商店(把成功商店的输入设为false)

连续失败时读取一次完整 job 日志,根据 HTTP 状态修复,不要盲目反复提交。审核中的商店应说明需等待,且不承诺审核时间。

十、完成标准与最终报告

一次完整发版需要留下可核验结果(SKILL.md 的"完成标准"):

  • manifest/manifest.json 是目标版本,版本 commit 已推送;按仓库惯例,版本 tag 指向该 bump commit;
  • 普通 CI、单元测试和 Playwright 测试通过;
  • 同名 GitHub Release 已发布,中文说明符合历史风格;
  • Release 的 Chrome、Firefox、Edge、Safari 四个附件上传成功;
  • 商店 workflow 的每个启用 job 都有明确结果:成功时记录审核状态,失败时记录 HTTP 状态和可执行修复;
  • 工作树干净且本地分支与远端同步。

最终向用户分别报告 GitHub Release、Chrome、Firefox 和 Edge 的状态,附 Actions run 链接,并严格区分四档状态,绝不把"上传成功"说成"已上线"。

附:与本文相关的仓库文件索引

  • 发版技能:.agents/skills/browser-extension-release/SKILL.md、references/release-process.md、references/store-publishing.md
  • 工作流:release.yml、publish-stores.yml、check-chrome-credentials.yml、ci.yml、tests.yml、safari-macos.yml
  • 版本与构建:manifest/manifest.json、webpack/webpack.manifest.js、package.json
  • 其他:docs/publishing.md、scripts/check-chrome-credentials.mjs、config.json.example、AGENTS.md
  • 前端

【免费下载链接】BilibiliSponsorBlock

一款跳过小电视视频中恰饭片段的浏览器插件,移植自 SponsorBlock。A browser extension to skip sponsored segments in videos, ported from the SponsorBlock

项目地址:https://gitcode.com/gh_mirrors/bi/BilibiliSponsorBlock
点击查看免费下载
上一篇:Torba钱包开发常见问题解答:新手必看的完整手册
下一篇:Realtek 8192FU Linux USB无线网卡驱动完整安装指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进