- 桌面应用
【免费下载链接】browser-laptop
[DEPRECATED] Please see https://github.com/brave/brave-browser for the current version of Brave
本篇技术指南围绕 browser-laptop(Brave 桌面浏览器,现已迁移至 brave/brave-browser 的旧版本仓库)的自动更新(Auto Update)体系展开,核心覆盖两条主线:一是基于 Electron/Squirrel 的更新检查、下载与安装的完整链路及其源码实现;二是 macOS 平台 dmg 构建产物在上线前必须完成的代码签名(Code Signing)全流程。读完本文,你将掌握本仓库中更新服务的工作原理(app/updater.js)、codesign签名与验证命令的完整用法、IDENTIFIER证书标识符等关键概念,并能复现从证书准备到签名验证、再到发布部署的完整工程流程。
一、自动更新机制总览(Overview)
原文档对自动更新的定义非常简洁:自动更新提供了一系列服务,允许浏览器自动检查更新,并在新版本可用时自动应用变更。
落到实现层面,这一能力由 Electron 框架内置的autoUpdater提供,而autoUpdater在 macOS / Windows 上的底层引擎正是Squirrel(Squirrel.mac / Squirrel.Windows)。在 app/updater.js 中可以清楚看到:
const autoUpdater = electron.autoUpdater const app = electron.app也就是说,browser-laptop 本身并不重复实现"下载安装包、替换可执行文件"这类系统级工作,而是将这一层委托给 Squirrel 运行时,项目自身聚焦于:
- 构建更新检查 URL(按渠道 / 版本 / 平台拼接);
- 按启动延时与周期频率调度检查;
- 与自建的更新元数据服务通信,获取最新版本信息;
- 把 Squirrel 反馈的状态(下载中、可用、无更新、错误等)映射为浏览器内的更新状态机,驱动 UI 提示;
- 在用户确认后调用
autoUpdater.quitAndInstall()完成退出重装。
文档同时强调了一个 macOS 关键约束:手工安装 dmg 后,后续更新自动进行;但 dmg 产物的各组件必须先完成数字签名,更新系统才会执行新版本检查与安装。这一点在第五节会有完整演练。
二、更新链路源码剖析:从启动初始化到状态机
自动更新并非凭空发生,它在浏览器启动时被显式初始化。在 app/index.js 中,应用加载完持久化状态后立即调用:
updater.init( process.platform, process.arch, process.env.BRAVE_UPDATE_VERSION || app.getVersion(), process.env.BRAVE_ENABLE_PREVIEW_UPDATES !== undefined ) // 菜单入口触发的手动检查 process.on(messages.CHECK_FOR_UPDATE, () => updater.checkForUpdate(true)) ipcMain.on(messages.CHECK_FOR_UPDATE, () => updater.checkForUpdate(true))updater.init拿到平台、架构、版本号与"是否接受预览版"标志后,在 app/updater.js 中依次完成:重置更新状态、拼接更新 URL、注册定时任务、设置 Squirrel 的 feed URL。
1. 更新 URL 的构建与平台映射
app/updater.js 定义了一份process.platform到更新 API 标识的映射表:
var platforms = { 'darwin': 'osx', 'win32x64': 'winx64', 'win32ia32': 'winia32', 'linux': 'linux' }在此基础上,updateUrl() 拼出完整的更新端点:
exports.updateUrl = function (updates, platform, arch) { if (platform === 'win32') { platform = platform + arch // win32x64 / win32ia32 } platformBaseUrl = `${updates.baseUrl}/${Channel.channel()}/${version}/${platforms[platform]}` ... }URL 形如{baseUrl}/{channel}/{version}/{platform},其中渠道与版本是更新路径的两个关键维度。渠道取自 app/channel.js,优先级为构建配置buildConfig.channel→ 环境变量CHANNEL→ 默认空串(dev),合法取值集合为dev / beta / stable / developer / nightly / ''。
2. 检查频率调度
scheduleUpdates() 设置了两种检查时机,具体间隔由 js/constants/appConfig.js 中的updates配置段控制:
| 配置项 | 默认值 | 含义 |
|---|---|---|
appUpdateCheckFrequency | 1000 * 60 * 60(1 小时) | 周期检查频率,通过setInterval触发 |
runtimeUpdateCheckDelay | 1000 * 60 * 2(启动后 2 分钟) | 启动后首次检查的延时,通过setTimeout触发 |
两个配置均为 0 或 falsy 时,对应定时任务不会注册。
3. 元数据请求与隐私保护设计
真正向更新服务器发起请求的是 requestVersionInfo()。值得注意的是,该请求采用了一种隐私优先的报文设计:不向服务器传递可识别个人的信息,而是由 paramsFromLastCheckDelta() 生成一组布尔标志,表达"距离上次检查的时间差":
daily:距上次检查是否超过一天;weekly:上周是否未检查;monthly:上月是否未检查;first:浏览器是否从未发起过更新请求;woi:安装周(week of installation);ref:来自updateState.getUpdateProp(state, 'referralPromoCode')的推广码(无则'none')。
服务器对请求的响应语义如下:返回204表示无可用更新,200且携带 JSON body 则触发UPDATE_META_DATA_RETRIEVED事件并进入下载处理回调 downloadHandler();网络错误或配置错误则向autoUpdater发出error事件。
4. 更新状态机与 UI 可见性
Squirrel 与元数据请求产生的事件,会被统一收敛到状态机。状态全集定义在 js/constants/updateStatus.js:
UPDATE_NONE、UPDATE_CHECKING、UPDATE_AVAILABLE、UPDATE_AVAILABLE_DEFERRED、UPDATE_DOWNLOADING、UPDATE_NOT_AVAILABLE、UPDATE_ERROR、UPDATE_APPLYING_RESTART、UPDATE_APPLYING_NO_RESTART。
app/updater.js 中注册了update-downloaded、UPDATE_AVAILABLE、UPDATE_NOT_AVAILABLE、error四个事件的处理器,逐一映射到上述状态。
UI 何时展示更新提示,由 app/common/state/updateState.js 的isUpdateVisible决定:后台检查(非 verbose)时只允许"更新可用"提示打扰用户;其余检查中、下载中等状态一律静默,避免频繁打扰。
5. 下载完成后的安装与关闭竞态处理
下载完成后,用户确认即调用updater.quitAndInstall()(app/updater.js),交由 Squirrel 完成退出与重装。为防止用户"下载完但不想立刻重启"的场景被误杀,app/sessionStoreShutdown.js 在关闭流程里做了状态标记:Windows 下置为UPDATE_APPLYING_RESTART(Squirrel 检测到应用正在关闭,避免用户二次手动打开),其他平台置为UPDATE_APPLYING_NO_RESTART,随后在退出回调中统一调用updater.quitAndInstall()。这也解释了状态枚举里两个APPLYING_*值的用途——它们在 app/common/state/updateState.js 中被判定为"不可见"状态。
三、macOS 更新约束:Squirrel.mac 与签名前提
原文档明确指出 macOS 平台的更新形态:
手工安装之后,所有后续更新将通过 Electron(Atom)框架提供的 Squirrel.mac 服务自动进行。构建出的 dmg 二进制产物必须完成组件数字签名,更新系统才会检查并安装新版本。
也就是说,签名是 macOS 自动更新的前置条件——未签名的 dmg 产物无法被更新通道识别和接受。这与 macOS 的 Gatekeeper / 代码签名体系一致:Squirrel.mac 在检查更新时会校验新版本的签名与 sealed resources 完整性。
四、签名相关术语定义
原文档给出了三个贯穿签名流程的核心定义,整理如下:
| 术语 | 定义 | 说明 |
|---|---|---|
USER | 当前登录用户 | 命令示例路径中的用户目录占位符 |
CERTIFICATE | 由第三方签发的文件,用于对二进制进行数字签名并确认其来源 | 即 Apple Developer 证书 |
IDENTIFIER | 证书中的组织标识符(10 位大写字母数字) | 形如12345ABCDE,codesign 用--sign指定 |
IDENTIFIER是签名成败的关键:它必须与 Keychain 中证书的 Team Identifier / Organizational Identifier 一致,否则codesign会报错找不到匹配的签名身份。
五、macOS 代码签名四步实战
原文档给出的签名流程共四步,以下按序完整展开,并补充每步的要点与当前仓库的对应物。
步骤 1:创建并下载开发者证书
- 登录 developer.apple.com,创建并下载开发者证书,得到
.cer格式文件; - 使用 macOS 自带的Keychain Access(钥匙串访问)程序,将证书导入login(登录)分区(Keychain Access 默认会处理
.cer文件)。
步骤 2:确保存在关联的私钥
- 确认步骤 1 安装的证书已关联一个私钥(证书条目下会展开并标注 private key);
- 生成私钥(
.p12文件)是多步骤过程,需要访问 developer.apple.com 门户并操作开发机,原文档引用了第三方 p12 生成教程(appfurnace 的《How do I make a p12 file?》一文),可按该思路在开发者门户中导出; - 若已持有私钥,将其导入 Keychain Access,并确保与开发者证书绑定在同一条目下。
要点:私钥与证书必须在同一登录钥匙串条目中关联,codesign才能找到可用的签名身份。
步骤 3:构建待签名的未压缩应用产物
原文档给出的命令为:
npm run build-darwin需要说明的是,当前仓库package.json(见 package.json)中实际保留的构建脚本入口是build-installer(node ./tools/buildInstaller.js)与build-package(node ./tools/buildPackage.js),产物目录名为Brave-darwin-x64/。无论采用哪条构建入口,产物都应包含完整的.app结构与Contents/Frameworks目录,以便执行后续签名。
步骤 4:对产物执行 codesign
签名分为两层,按目录层级依次执行(以下命令为原文档原样):
第一层——Frameworks 目录(路径Brave-darwin-x64/Contents/Brave.app/Contents/Frameworks):
codesign --deep --force --strict --verbose --sign IDENTIFIER *第二层——app 根目录(路径Brave-darwin-x64):
codesign --deep --force --strict --verbose --sign IDENTIFIER Brave.app/命令参数含义速查:
| 参数 | 作用 |
|---|---|
--deep | 对 app 内部所有嵌套组件(Frameworks、Helpers 等)递归签名 |
--force | 覆盖已存在的签名 |
--strict | 启用严格模式校验 |
--verbose | 输出详细签名日志 |
--sign IDENTIFIER | 指定签名身份(证书组织标识符) |
替代方案:一键执行签名脚本
文档指出,以上两步可以通过仓库自带脚本一次性完成,签名身份以环境变量方式注入:
IDENTIFIER=12345ABCDE npm run build-installer脚本入口是 tools/buildInstaller.js。从源码看,该脚本在 Darwin 分支下会依次完成:校验IDENTIFIER是否已设置(未设置直接raiseError('IDENTIFIER needs to be set to the certificate organization'),见 tools/buildInstaller.js)→ 为 Widevine(Brave Framework)生成并校验签名 → 对 Frameworks 目录执行codesign --deep --force --strict --verbose --sign $IDENTIFIER *→ 对Brave.app/执行同一签名命令 → 依据渠道(res/{channel}/builderConfig.json)打包 dmg/pkg → 用ditto生成用于更新的 zip 产物(dist/Brave-{version}.zip)。
也就是说,npm run build-installer把"签名 + 打包 + 生成更新 zip"串成了单条流水线,正是文档第 4 步命令被脚本化的落地实现。
六、签名结果验证(Check)
签名是否成功,必须用验证命令确认,而不是凭感觉。原文档给出两段验证手段与示例输出,下面完整保留并逐段解读。
1. 详细信息校验:codesign -dvv
codesign -dvv Brave.app/示例输出(原文档摘录,路径中的USER为当前登录用户占位):
Executable=/Users/USER/repos/browser-electron/Brave-darwin-x64/Brave.app/Contents/MacOS/Electron Identifier=com.electron.XXXX Format=bundle with Mach-O thin (x86_64) CodeDirectory v=20200 size=202 flags=0x0(none) hashes=3+3 location=embedded Signature size=4385 Authority=3rd Party Mac Developer Application: XXXX Software, Inc. (IDENTIFIER) Authority=Apple Worldwide Developer Relations Certification Authority Authority=Apple Root CA Signed Time=Dec 9, 2015, 5:29:13 PM Info.plist entries=21 TeamIdentifier=IDENTIFIER Sealed Resources version=2 rules=12 files=58929 Internal requirements count=1 size=208解读要点:
Authority链展示了完整的证书信任链:应用证书 → Apple Worldwide Developer Relations → Apple Root CA,这是签名被系统信任的基础;TeamIdentifier应与你的IDENTIFIER一致;Sealed Resources version=2是文档特别强调的检查点:version 必须是 2,若显示为 1 则说明签名方式不合规(例如被旧工具链签名),会破坏 Squirrel 更新校验。
2. 深度验证:codesign --deep-verify --verbose=4
codesign --deep-verify --verbose=4 Brave.app/该命令对 app 内所有嵌套组件逐一校验,示例输出中每个组件都有--prepared:与--validated:两行记录,涵盖:
Brave Helper.app(含 EH / NP 变体及其Contents/MacOS可执行文件);ReactiveCocoa.framework、Mantle.framework、Squirrel.framework(Squirrel 是自动更新引擎本身,位于Contents/Frameworks);Electron Framework.framework。
全部组件校验完成后,最终两行结论性输出是:
Brave.app/: valid on disk Brave.app/: satisfies its Designated Requirement只有同时满足"磁盘上有效"与"满足 Designated Requirement",签名才算真正通过,更新通道才会接纳该产物。
七、验证通过后的更新入口
签名完成且 dmg 安装后,用户即可通过菜单入口触发更新检查。回顾 app/index.js,主进程同时监听了process事件与ipcMain的CHECK_FOR_UPDATE消息,均调用updater.checkForUpdate(true)(verbose=true,即允许弹出检查 UI)。
更新过程中的日志会被追加写入用户数据目录下的updateLog.log(见 app/updater.js),格式为ISO 时间戳 - 内容,内容包括platformBaseUrl、updateUrl、lastCheckYMD等诊断信息,排查更新问题时是首选的第一手日志。
八、Windows x64 与 Linux 平台现状
原文档在 Windows x64 一节仅标注了TODO,说明签名与安装细节在当时尚未成文。但更新链路并非完全空白,从 app/updater.js 源码结构看,Windows 更新逻辑已具备:
- Windows 的更新 URL 不使用统一拼接,而是走独立的
winBaseUrl(${winUpdateHost}/multi-channel/releases/CHANNEL/,winUpdateHost默认https://download.brave.com),并将架构后缀拼在其后(app/updater.js、js/constants/appConfig.js); - Windows 平台在拿到元数据后,会先读取
metadata.braveURL重新设置 feed URL 再发起checkForUpdates()(app/updater.js),这暗示 Windows 更新端点与元数据服务是分离的两套地址; - 关闭流程中对 Windows 单独标记
UPDATE_APPLYING_RESTART,以避免用户二次手动启动(app/sessionStoreShutdown.js)。
从源码结构看,Linux 平台同样会构造更新端点(platforms.linux = 'linux'且走platformBaseUrl分支),但自动更新的安装动作仍以 Squirrel 支持的 macOS / Windows 为完整闭环。
九、更新部署(Deploying Updates)
原文档关于部署的指引指向一个独立的更新服务端项目:brave/vault-updater。也就是说,browser-laptop 仓库只负责"客户端检查 + 签名 + 打包";新版本 zip / dmg 产物的托管、版本元数据的对外提供,由该独立的更新服务承担。客户端请求的元数据端点即 js/constants/appConfig.js 中的baseUrl: ${updateHost}/1/releases(updateHost默认https://laptop-updates.brave.com)。发布流程通常为:签名打包产物 → 上传至更新服务 → 更新服务对外发布新版本元数据 → 客户端周期检查命中后完成升级。
十、更新相关配置与环境变量速查
综合 js/constants/appConfig.js、app/channel.js 与 app/index.js,与自动更新相关的全部可调参数汇总如下:
| 配置 / 环境变量 | 默认值 | 作用 |
|---|---|---|
updates.appUpdateCheckFrequency | 1 小时 | 周期更新检查间隔 |
updates.runtimeUpdateCheckDelay | 启动后 2 分钟 | 启动首查延时 |
updates.autoAppUpdate | false | 是否在更新加载前静默(不通知用户) |
updates.autoRuntimeUpdate | false | 运行时更新是否静默 |
updates.baseUrl | ${BRAVE_UPDATE_HOST}/1/releases | macOS/Linux 更新元数据端点 |
updates.winBaseUrl | ${BRAVE_WIN_UPDATE_HOST}/multi-channel/releases/CHANNEL/ | Windows 独立更新端点 |
BRAVE_UPDATE_HOST | https://laptop-updates.brave.com | 覆盖更新元数据主机 |
BRAVE_WIN_UPDATE_HOST | https://download.brave.com | 覆盖 Windows 更新主机 |
BRAVE_UPDATE_VERSION | 取app.getVersion() | 覆盖用于更新 URL 的版本号 |
BRAVE_ENABLE_PREVIEW_UPDATES | 未定义 | 定义后接受预览版更新(URL 追加accept_preview=true) |
CHANNEL | 构建配置或空串(dev) | 指定发布渠道,合法值dev/beta/stable/developer/nightly |
IDENTIFIER(签名时) | 无 | 签名身份,build-installer的必需环境变量 |
适用前提:上述行为基于当前仓库代码(版本0.27.3,见 package.json)验证。该仓库已标记为 DEPRECATED(当前版本见 brave/brave-browser),本文所有命令与路径均以本仓库实际内容为准。
结语
自动更新看似是"调一个 Squirrel API"那么简单,但真正让它在生产环境稳定运转,靠的是三条链路:签名链(证书、私钥、codesign 与验证输出)、调度链(启动延时 + 周期检查 + 隐私参数化的元数据请求)与状态链(更新状态机、UI 可见性与关闭时的重启安装竞态处理)。本文从 docs/autoUpdates.md 出发,逐一打通了这三条链路在仓库中的实现与配置,无论是想复现 macOS 签名发布流程,还是排查更新不生效的问题(先看updateLog.log,再核对渠道、版本与签名),都能在本文与所列源码路径中找到落点。
- 桌面应用
【免费下载链接】browser-laptop
[DEPRECATED] Please see https://github.com/brave/brave-browser for the current version of Brave
相关推荐
3步告别数据混乱:DataEase开源BI工具让数据可视化如此简单
3步告别数据混乱:DataEase开源BI工具让数据可视化如此简单 你是否曾为海量数据而头疼?面对Excel表格里密密麻麻的数字,却无法快速洞察业务趋势?每天需
数据分析数据可视化后端前端brave/browser-laptop事件处理机制:从用户输入到状态更新
brave/browser laptop事件处理机制:从用户输入到状态更新 事件处理机制概述 Brave浏览器的事件处理机制是连接用户操作与应用状态变化的核心桥
桌面应用brave/browser-laptop代码质量保障:ESLint配置与代码审查
brave/browser laptop代码质量保障:ESLint配置与代码审查 在开源项目开发中,代码质量保障是确保项目可维护性和稳定性的核心环节。brave
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考