AdGuard Browser Extension 部署指南:基于 Bamboo 的多管道构建与发布体系深度解析

原创2026-10-02 09:50:201,451 阅读
文章标签:前端网络安全

AdGuard Browser Extension 部署指南:基于 Bamboo 的多管道构建与发布体系深度解析

AdGuard Browser Extension 是一个同时面向 Chrome、Edge、Firefox、Opera 等浏览器分发的开源扩展,其构建产物既要发布到各浏览器官方商店(Chrome Web Store、Mozilla Add-ons、Edge Add-ons),又要自托管到官方静态服务器用于自分发更新,还要附带 Chrome 的 CRX 自更新通道与 GitHub Release 附件。为了支撑如此复杂的多渠道发布,项目在 DEPLOYMENT.md 中系统性地定义了以 Atlassian Bamboo 为核心的构建/部署管道体系。本文以该文档为主体,结合仓库内真实的 Bamboo 配置、Dockerfile 多阶段构建与工具脚本,完整讲解 beta、release 以及两条 Firefox beta 管道的设计思路、部署目标、手动发布步骤与故障排查方法,帮助读者掌握从版本提升、本地校验到云端签名、多渠道部署的完整链路。

管道总览:四类构建与部署计划

DEPLOYMENT.md 用一张表格概括了整个仓库的 CI/CD 拓扑:每一类产物都由独立的 Build plan(构建计划) 和 Deploy plan(部署计划) 组成,二者通过 Bamboo 的 source-plan 字段关联。

类型 Build plan Deploy plan 部署目标
Beta ADGEXT-BEBETASPECS deploy beta static.adtidy.org、Chrome WebStore MV3、GitHub
Release ADGEXT-BERELEASESPECS deploy release static.adtidy.org、Chrome WebStore MV2、Chrome WebStore MV3、Addons Mozilla、Edge Add-ons、GitHub
Firefox standalone beta ADGEXT-BEFIREFOXSTANDALONEBETASPECS deploy firefox standalone beta static.adtidy.org、GitHub
Firefox AMO beta ADGEXT-BEFIREFOXAMOBETASPECS deploy firefox amo beta Addons Mozilla

所有计划定义在 bamboo-specs/ 目录中,并通过 bamboo-specs/bamboo.yaml 统一注册。该文件本身并不包含计划内容,而是通过 YAML 的 !include 指令按顺序导入各计划:

  • 构建计划:build-beta.yaml、build-release.yaml、build-firefox-standalone-beta.yaml、build-firefox-amo-beta.yaml;
  • 部署计划:deploy-beta.yaml、deploy-release.yaml、deploy-firefox-standalone-beta.yaml、deploy-firefox-amo-beta.yaml;
  • 权限计划:permissions-beta.yaml、permissions-release.yaml、permissions-firefox-standalone-beta.yaml、permissions-firefox-amo-beta.yaml;
  • 配套计划:tests.yaml、e2e-tests.yaml、increment.yaml(版本自增)、auto-build.yaml、auto-publish.yaml 及其权限文件。

由此可以看出,AdGuard 的发布体系遵循“一个发布渠道 = 一个构建计划 + 一个部署计划 + 一份权限声明”的三件套模式,四个渠道互不干扰。

Beta 管道:构建与部署的完整生命周期

构建计划:两阶段校验

Beta 的构建计划 ADGEXT-BEBETASPECS(定义于 bamboo-specs/build-beta.yaml)包含两个 stage:

  1. Size and locales check(尺寸与语言包校验):并行执行两个 job——
    • Check Bundle Size:通过 Docker 目标 bundle-size-check-output 运行 pnpm ${BUILD_TYPE} --zip 与 pnpm check-bundle-size beta;
    • Check translations:通过 Docker 目标 locales-check-output 运行 pnpm locales validate --min。
  2. Build(构建):通过 Docker 目标 beta-build-output 执行 pnpm beta --zip,产出 build.txt、chrome.zip、chrome-mv3.zip、edge.zip;随后再通过 beta-chrome-crx-output 目标执行 pnpm beta chrome-crx,额外产出 chrome.crx 与 update.xml(用于 Chrome 的自更新通道)。

构建完成后,计划还会执行 VCS 打标签任务,生成 v${bamboo.inject.version}-beta 标签,并将构建号注入到后续部署步骤中。所有产物声明为 shared: true 的共享产物,供部署计划下载。

部署计划:三个环境

Beta 的部署计划定义于 bamboo-specs/deploy-beta.yaml,source-plan: ADGEXT-BEBETASPECS,release-naming: ${bamboo.inject.version}-beta,包含三个 environment:

  • static.adtidy.org:下载 chrome.zip、chrome-mv3.zip、build.txt、chrome.crx、update.xml,然后执行 ./bamboo-deploy-publisher/deploy.sh chrome-extension-beta 发布 Chrome beta 扩展包;
  • Chrome WebStore MV3:仅下载 chrome-mv3.zip,执行 deploy.sh browser-extension-webstore-beta,将 MV3 beta 发布到 Chrome Web Store 的 Experimental(实验性)MV3 条目;
  • GitHub:下载 chrome.zip、chrome-mv3.zip、edge.zip、chrome.crx、build.txt,使用 Bamboo 变量 bamboo.githubPublicRepoPassword 作为 GitHub Token,执行 deploy.sh browser-extension-github-beta,将各浏览器 bundle 附件到 GitHub beta Release。

每个环境的任务模板高度一致:clean → checkout 部署发布器仓库(bamboo-deploy-publisher)→ artifact-download 按需拉取产物 → 执行 deploy.sh 子命令。这种“一次构建、按环境挑产物”的写法是整组部署 YAML 的通用模式。

Release 管道:覆盖全部商店渠道

构建计划

Release 构建计划 ADGEXT-BERELEASESPECS(bamboo-specs/build-release.yaml)与 beta 结构相同(Size check + Build 两个 stage),但构建覆盖所有浏览器目标:Chrome MV2、Chrome MV3、Edge、Opera、Opera MV3、Firefox AMO、Firefox standalone。构建产物包括 chrome.zip、chrome-mv3.zip、edge.zip、opera.zip、opera-mv3.zip、firefox.zip(由 firefox-amo.zip 重命名而来)、source.zip 与 approval-notes.txt。同样会执行 inject-variables 与 VCS 打标签(v${bamboo.inject.version})。

部署计划:六个环境

Release 部署计划定义于 bamboo-specs/deploy-release.yaml,release-naming 不再带 -beta 后缀,环境清单:

  • static.adtidy.org:同时执行 deploy.sh chrome-extension-release 与 deploy.sh firefox-extension-release。DEPLOYMENT.md 特别强调:此位置必须同时保留 chrome.zip 和 chrome-mv3.zip,因为不同浏览器(例如 Brave)会从这里拉取静态构建包使用;
  • Chrome WebStore MV2:下载 chrome.zip,执行 deploy.sh browser-extension-webstore-release-mv2;
  • Chrome WebStore MV3:下载 chrome-mv3.zip,执行 deploy.sh browser-extension-webstore-release;
  • Addons Mozilla:下载 firefox.zip、source.zip、approval-notes.txt,执行 deploy.sh browser-extension-amo 向 AMO 提交;
  • Edge Addons:下载 edge.zip,执行 deploy.sh browser-extension-edge 向微软 Edge 插件商店提交;
  • GitHub:下载全部 bundle 与 build.txt,执行 deploy.sh browser-extension-github-release 发布 GitHub Release。

注意 Chrome WebStore 的 MV2 与 MV3 是两个独立环境、独立执行脚本;而 AMO 渠道之所以需要 source.zip 与 approval-notes.txt,是因为 Mozilla 的审核流程要求附带源码包与构建复现说明(详见后文“源码归档”小节)。

Firefox Beta 双管道:为什么必须拆分

Firefox beta 存在两种彼此独立的分发形态:

  • Standalone(独立签名版):由 Mozilla 通过 go-webext 签名后自分发,用户通过 update.json 更新通道自行安装;
  • AMO(商店托管版):提交到 Mozilla Add-ons 审核列表,由 AMO 在审核期间完成签名。

DEPLOYMENT.md 用专门一节解释了“Why two Firefox beta pipelines”:standalone 版需要等待 Mozilla 的签名服务,go-webext 在等待期间最长可阻塞到 20 分钟的 CI 超时;此前两种形态共用同一套构建/部署计划,一次缓慢或失败的签名会连带阻塞 AMO 的提交。拆分后,AMO 的构建与提交完全不受 standalone 签名状态影响,反之亦然。

Standalone Firefox Beta 管道

构建计划 ADGEXT-BEFIREFOXSTANDALONEBETASPECS(bamboo-specs/build-firefox-standalone-beta.yaml)只有一个 job Build and Sign Standalone,通过两次 docker build 完成:

  1. 先构建 firefox.zip(Docker 目标 firefox-beta-build-output,对应 Dockerfile 中 pnpm beta firefox-standalone --zip);
  2. 再用 adguard/extension-builder:22.22--0.4.1--0 镜像(预装了 go-webext)执行签名(Docker 目标 firefox-beta-sign-output),产出 firefox.xpi 与 update.json。

签名阶段通过 --build-context firefox-artifacts=output/artifacts 复用第一步的产物,AMO 凭据通过 --build-arg FIREFOX_CLIENT_ID 与 --secret id=FIREFOX_CLIENT_SECRET 传入。签名命令为:

go-webext -v sign firefox -f 'firefox.zip' -s 'source.zip' -o 'firefox.xpi' -n "$(cat approval-notes.txt)"

共享产物为 build.txt、firefox.zip、firefox.xpi、update.json。当 Mozilla 签名缓慢触发 20 分钟超时时,重跑同一次构建即可——重跑是安全的,因为幂等性位于 go-webext 而非 Bamboo:第二次运行时它会检测到该版本已上传至 Mozilla,跳过重复上传、轮询签名状态并下载已签名的 .xpi。管道层面没有任何需要重试的逻辑。

部署计划 browser extension - deploy firefox standalone beta(bamboo-specs/deploy-firefox-standalone-beta.yaml):

  • static.adtidy.org → deploy.sh firefox-extension-beta,更新通道产物为 firefox.zip、firefox.xpi、update.json、build.txt;
  • GitHub → deploy.sh browser-extension-github-beta-firefox,将 firefox.zip、firefox.xpi 追加到 GitHub beta Release。

AMO Firefox Beta 管道

构建计划 ADGEXT-BEFIREFOXAMOBETASPECS(bamboo-specs/build-firefox-amo-beta.yaml)只有一个 job Build AMO:通过 Docker 目标 firefox-amo-beta-build-output 执行 pnpm beta firefox-amo --zip,产出未签名的 firefox-amo.zip。AMO 托管的附加组件不在本地签名,由 Mozilla 在审核时签名,因此该管道完全不受 go-webext 阻塞。

共享产物为 build.txt、firefox-amo.zip、source.zip、approval-notes.txt。部署计划 browser extension - deploy firefox amo beta(bamboo-specs/deploy-firefox-amo-beta.yaml)只有 Addons Mozilla 一个环境,执行 deploy.sh browser-extension-amo-beta,将 firefox-amo.zip + source.zip + approval-notes.txt 一并提交给 AMO 审核。

如何发布一个 Firefox Beta

两个 build plan 都声明了 triggers: [] 与 branches.create: manually,意味着只能由人在 Bamboo 上手动触发。DEPLOYMENT.md 给出的操作顺序:

  1. 同时运行 standalone 与 AMO 两个构建计划(二者独立、可并行);
  2. 各自完成后运行对应的部署计划,两个部署计划相互独立,可按任意顺序单独或同时执行;
  3. 若 standalone 签名超时,不要阻塞 AMO,让 AMO 照常提交;待 Mozilla 签名完成后重跑 standalone 构建以取回签名后的 .xpi。

准备一次新发布:七个关键步骤

DEPLOYMENT.md 指出,以下步骤对 beta 与 release 均适用,差异点会分别标注。下面结合 package.json 中真实存在的脚本逐一展开。

  1. 在 package.json 中提升依赖库版本。需要更新的通常是 @adguard/tsurlfilter、@adguard/tswebextension、@adguard/scriptlets 等近期发布过的库。当前仓库中的依赖版本可作参照:@adguard/tsurlfilter: 6.0.3、@adguard/tswebextension: 5.0.3、@adguard/scriptlets: 2.5.1。命名规则:beta 使用 -beta 后缀(如 5.5.0-beta),release 使用正式版本(如 5.5.0)。

  2. 设置 TSURLFILTER_REF="" 以禁用本地链接。仓库内的 bamboo-specs/scripts/clone-tsurlfilter.sh 默认 TSURLFILTER_REF="",此时不会克隆 tsurlfilter 源码、直接使用 npm 版本;只有当该变量指向某个分支、commit 或 tag 时,才会用 git archive 克隆干净的源码目录并通过 bamboo-specs/scripts/link-tsurlfilter.sh 以 pnpm link 的方式链接本地构建的 tswebextension(可选附带 agtree、tsurlfilter、dnr-converter、css-tokenizer)。发布前必须置空,因为库已发布到 npm。clone-tsurlfilter.sh 的注释还解释了为什么用 git archive 而非裸 clone:Docker 会对 --build-context 中每个文件做 checksum 以决定缓存是否命中,而裸 clone 的 .git 元数据每次克隆都不同、必然击穿缓存,git archive 则能产出字节一致的输出,从而让 tsurlfilter 构建阶段命中缓存、耗时趋近于 0。

  3. 运行 pnpm dev 确保 scriptlets 脚本是最新的(对应 package.json 中 "dev": "cross-env BUILD_ENV=dev tsx tools/bundle.ts")。

  4. 运行 pnpm resources && pnpm resources:mv3 更新过滤规则、公共后缀列表与 DNR ruleset。resources:mv3 脚本会先 pnpm remove @adguard/dnr-rulesets、再 pnpm add -D @adguard/dnr-rulesets@~5.0,最后执行 tools/resources-mv3.ts,产物落入 Extension/filters/ 下的各浏览器目录(如 Extension/filters/chromium-mv3/declarative/ 的各个 ruleset_* JSON)。

  5. 构建并检查包体积:

    • Beta:pnpm beta --zip,然后 pnpm check-bundle-size beta;
    • Release:pnpm release --zip,然后 pnpm check-bundle-size release。

    若尺寸差异在可接受范围内,运行 pnpm update-bundle-size beta(或 release)更新基准;否则需排查体积增长原因。体积检查的实现见 tools/bundle-size/check.ts 与 tools/bundle-size/constants.ts:

    常量 值 含义
    DEFAULT_SIZE_THRESHOLD_PERCENTAGE 10 默认允许的体积增长阈值(百分比),可由 CLI --threshold 覆盖
    MAX_MV3_SIZE_BYTES 30 MB Chrome MV3 构建的硬性上限
    MAX_FIREFOX_AMO_SIZE_BYTES 200 MB AMO 提交整体大小上限
    MAX_FIREFOX_SIZE_BYTES 4 MB AMO 对 zip 内单个文件的限制(Firefox 浏览器本身并不强制)

    检查工具同时还会检测 MV3 bundle 是否超限、通过 pnpm why 检测重复的包版本,并把历史数据写入 .bundle-sizes.json;tools/bundle-size/update.ts 负责用最新构建统计刷新该基准文件。

  6. 运行 pnpm locales validate 校验翻译文件,如发现问题则创建翻译任务分发给译者。Bamboo 构建中对应的自动化校验是 Docker 目标 locales-check-output 内的 pnpm locales validate --min。

  7. 合并 PR 并在 Bamboo 中触发构建。

此外,仓库还通过 increment.yaml(bamboo-specs/increment.yaml)在每次构建后自动递增版本号:构建产物 increment-output 会把修改后的 package.json 复制回工作树并执行 VCS commit,提交信息为 skipci: Automatic increment build number。package.json 中的版本号 5.5.2+3.build.20260825090125 正是该机制的产物。

部署权限控制

每个部署计划都配有对应的 permissions-*.yaml,遵循“部署计划 name 必须与权限文件中的 deployment.name 完全一致”的约束。以 bamboo-specs/permissions-firefox-standalone-beta.yaml 为例:

  • 部署级权限:extensions-developers 与 adguard-qa 两个组拥有 view 权限;
  • 环境级权限:static.adtidy.org 与 GitHub 环境仅对 extensions-developers 开放 view + deploy。

permissions-firefox-amo-beta.yaml 采用相同模式,仅环境改为 Addons Mozilla。因此,当某个部署计划报权限错误时,应优先核对部署 YAML 的 deployment.name 与权限 YAML 的 deployment.name 是否逐字符一致(这是 Troubleshooting 一节明确给出的排查方向之一)。

源码归档与 AMO 审核配套产物

Firefox 渠道(AMO 与 standalone 签名)都要求提交 source.zip 和 approval-notes.txt,二者由 bamboo-specs/scripts/archive-source.sh 在 Docker 构建阶段生成。该脚本的要点:

  • 打包全部源码文件,排除 .git/、.gitignore 命中的模式,以及 Extension/filters/ 目录(但保留 Extension/filters/firefox 的过滤规则,因为 Firefox 构建需要它们);
  • 在 source.zip 内的 README.md 末尾追加 “Building Instructions for Firefox Add-ons Review Team” 章节,给出仅依赖 Docker 的构建复现命令,例如:
    docker run --rm \
        -v "$(pwd)":/workspace \
        -w /workspace \
        adguard/extension-builder:22.22--0.4.1--0 \
        bash -c "pnpm install && pnpm release firefox-amo"
    
    并分别列出 AMO beta(扩展 ID adguardadblockeramobeta@adguard.com)与 standalone beta(扩展 ID adguardadblockerbeta@adguard.com)对应的 pnpm beta firefox-amo / pnpm beta firefox-standalone 命令;
  • 同步生成纯文本的 approval-notes.txt,通过 AMO API 的 approval_notes 字段提交,让审核员无需解包源码即可看到构建说明。

archive-source.sh 依赖同目录下的 bamboo-specs/scripts/generate-find-excludes.sh(在宿主机上、.git 可用时生成 gitignore-excludes.txt)与 parse-gitignore.sh(容器内 .git 不可用时的回退方案)。

Docker 多阶段构建:管道背后的构建矩阵

所有构建产物都来自仓库根目录的 Dockerfile,采用多阶段构建,配合 Bamboo YAML 中 docker build --target ... 的调用方式。以下 stage 与 DEPLOYMENT.md 描述的管道一一对应:

Docker 目标 对应计划/阶段 主要产物
bundle-size-check-output beta/release 的 Size check 体积检查标记文件
locales-check-output beta/release 的 Check translations 语言包校验标记文件
beta-build-output Beta Build build.txt、chrome.zip、chrome-mv3.zip、edge.zip
beta-chrome-crx-output Beta Build(第二阶段) chrome.crx、update.xml
firefox-beta-build-output Firefox standalone beta 构建 firefox.zip(由 firefox-standalone.zip 重命名)、update.json、source.zip、approval-notes.txt
firefox-beta-sign-output Firefox standalone beta 签名 firefox.xpi(依赖 --build-context firefox-artifacts)
firefox-amo-beta-build-output Firefox AMO beta 构建 firefox-amo.zip、source.zip、approval-notes.txt
release-build-output Release Build 全部浏览器 zip + source.zip + approval-notes.txt
increment-output increment 计划 修改后的 package.json

Dockerfile 还包含 tsurlfilter-build stage:当 TSURLFILTER_REF 非空时,从命名构建上下文复制 tsurlfilter 源码并执行 pnpm install + lerna run build,构建 @adguard/tswebextension 与 @adguard/dnr-rulesets;为空时则跳过,直接使用 package.json 声明的 npm 版本(对应发布流程步骤 2)。

Troubleshooting:常见故障与处理

DEPENDENCY.md 的 Troubleshooting 一节针对四个高频问题给出明确处理路径:

  • Standalone 签名超时(20 分钟):Mozilla 响应慢时的预期行为,不影响 AMO。等 Mozilla 完成签名后重跑同一构建计划即可取回 .xpi。幂等性在 go-webext 侧:第二次运行会识别出版本已上传、跳过重复上传、轮询签名状态并下载签名产物;Bamboo 只是再次调用同一个签名步骤,管道层面无需任何重试逻辑。
  • 部署失败、缺少产物:多半是部署计划在源构建计划完成之前被触发,或构建本身失败。确认源构建计划已产出全部共享产物后重跑部署。
  • AMO 与 standalone 相互干扰:理论上不应发生——它们是独立的计划与独立产物。若出现串扰,逐个核对每个部署计划的 source-plan 是否指向正确的构建计划,并确认两个构建计划基于同一 commit/版本运行。
  • 部署计划权限错误:部署 YAML 中的 name 必须与对应权限 YAML(permissions-firefox-standalone-beta.yaml / permissions-firefox-amo-beta.yaml)中的 deployment.name 完全一致。

小结

AdGuard Browser Extension 的发布体系是“Bamboo 计划编排 + Docker 多阶段构建 + 部署发布器脚本”三层结构的典型实践:构建阶段统一在 Docker 内完成、产出可共享的制品;部署阶段按渠道挑取产物、分发到自托管静态服务器、各浏览器商店与 GitHub Release;Firefox 特有的“独立签名”与“AMO 审核”双管道通过彻底解耦来规避签名阻塞。对于希望理解该项目发布机制、或参考其 CI/CD 设计的读者,建议从 DEPLOYMENT.md 入手,对照本文涉及的 bamboo-specs/bamboo.yaml、bamboo-specs/build-*.yaml、bamboo-specs/deploy-*.yaml、Dockerfile 与 tools/bundle-size/ 逐一阅读,即可还原整条从“提交代码”到“商店上架”的完整链路。

登录后查看全文
AdguardBrowserExtension