LobeHub Desktop 本地更新测试指南:基于 stable/nightly/canary 三渠道的端到端验证方案

📅 发布时间:2026/9/7 18:41:15
LobeHub Desktop 本地更新测试指南:基于 stable/nightly/canary 三渠道的端到端验证方案 LobeHub Desktop 本地更新测试指南基于 stable/nightly/canary 三渠道的端到端验证方案【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub本指南完整讲解 LobeHub Desktop 应用位于apps/desktop如何在本机搭建一套零成本的更新链路测试环境通过脚本在本地生成不同版本号的更新 manifest{channel}-mac.yml并启动静态服务器模拟分发源从而在接入真实发布渠道之前端到端验证发现新版本 → 切换渠道 → 降级回滚 → 下载失败等核心更新场景。阅读并实操本文后你将掌握scripts/update-test目录下全部脚本的用法、channel 切换的实现原理基于electron-updater的 generic provider 与setFeedURL以及本地未签名构建在 macOS 下的验证边界与签名注意事项。背景Desktop 应用的多渠道更新体系LobeHub Desktop 的自动更新由主进程中的 UpdaterManager 统一管理它基于electron-updater封装。整个体系围绕三个关键点设计渠道Channelstable/nightly/canary三档各自对应不同的 feed URL 与 manifest 文件名如stable-mac.yml、nightly-mac.yml。构建期的默认渠道由环境变量UPDATE_CHANNEL决定运行时则可通过设置页切换并持久化到本地 store。分发源Feed所有渠道统一走 generic HTTP providerURL 规则为{base}/{channel}/其中 base 取自环境变量UPDATE_SERVER_URL生产环境形如https://releases.lobehub.com。切换即重配渠道一旦切换UpdaterManager 会调用configureUpdateProvider()重新setFeedURL()并在下一次检查时读取对应渠道的 manifest。apps/desktop/scripts/update-test目录正是为这条更新链路量身打造的本机测试工具箱其 README 即 apps/desktop/scripts/update-test/README.md。目录内文件与职责如下文件职责setup.sh一键初始化创建三渠道目录、示例 manifest 与本地测试配置模板run-test.sh一键启动测试推荐自动完成生成 manifest → 启动服务器 → 配置应用 → 启动应用start-server.sh在指定端口默认8787后台启动本地静态服务器stop-server.sh停止本地服务器并清理 PID 文件generate-manifest.sh基于构建产物生成各渠道 manifest含 SHA512、releaseNotesdev-app-update.local.yml本地测试用的更新配置模板generic provider 指向http://localhost:8787/stableserver/服务器文件目录自动生成含stable/、nightly/、canary/三个子目录及对应 manifest核心原理channel 切换在源码中如何落地要理解测试脚本为什么这样写先看 UpdaterManager.ts 中与渠道强相关的两段实现。feed URL 与渠道的绑定configureUpdateProvider() 每次被调用时都会做三件事将 base URL 中可能残留的渠道后缀剥掉、把当前渠道拼回 feed URL、再通过setFeedURL以 generic provider 形式写入private getBaseUpdateUrl(): string | undefined { if (!UPDATE_SERVER_URL) return undefined; return UPDATE_SERVER_URL.replace(/\/(stable|nightly|canary|beta)\/?$/, ); } private configureUpdateProvider() { const baseUrl this.getBaseUpdateUrl(); if (baseUrl) { const feedUrl ${baseUrl}/${this.currentChannel}; autoUpdater.channel this.currentChannel; autoUpdater.setFeedURL({ provider: generic, url: feedUrl }); } else { // 未配置 UPDATE_SERVER_URL 时回退到 GitHub provider本地开发默认路径 autoUpdater.setFeedURL({ owner: lobehub, provider: github, repo: lobehub }); } }这正是文档中反复强调必须设置UPDATE_SERVER_URL环境变量的根本原因一旦缺失configureUpdateProvider()会带着当前渠道走 GitHub 分支本地测试就会去请求真实 GitHub Release 而非本地服务器导致验证结果失真。同时UPDATE_SERVER_URL作为 base与 manifest 文件名{channel}-mac.yml共同决定了 feed 地址语义例如 canary 渠道会去取http://localhost:8787/canary/canary-mac.yml。升级与降级的自动判定switchChannel() 是设置 Beta页切换渠道时触发的逻辑。除了重配 provider它还处理两个关键状态public switchChannel (channel: UpdateChannel) { this.currentChannel channel; autoUpdater.allowPrerelease channel ! stable; this.configureUpdateProvider(); // configureUpdateProvider 内部的 channel setter 会副作用修改 allowDowngrade // 因此这里必须重新置回 true保证从 canary 降回 stable场景可被检测到 autoUpdater.allowDowngrade true; ... };从源码可以总结出渠道语义stable → nightly / canaryallowPrerelease会被置为true应用以预发布身份去匹配带-nightly.x/-canary.x后缀的更高版本canary → stableallowPrerelease变回false同时allowDowngradetrue让低版本号的 stable 包也能触发降级更新。其余基础配置定义在 configs.tsexport const UPDATE_SERVER_URL getDesktopEnv().UPDATE_SERVER_URL; export const updaterConfig { app: { autoCheckUpdate: true, autoDownloadUpdate: true, checkUpdateInterval: 60 * 60 * 1000, // 每小时自动检查一次 }, enableAppUpdate: !isDev, // 开发模式isDevtrue下更新功能不初始化 };由此可以推断两个测试约束一是启动后约 60 秒会触发首次自动检查setTimeout(() this.checkForUpdates(), 60 * 1000)二是enableAppUpdate: !isDev意味着纯bun run dev开发态下 updater 不会初始化只能测 UI 与 IPC——这也是为什么下文区分开发模式与打包模式两种测试路径。目录结构脚本运行后会在server/下自动生成以下结构以仓库实际布局为准完整路径为apps/desktop/scripts/update-test/apps/desktop/scripts/update-test/ ├── README.md # 本文对应的原始指南 ├── setup.sh # 一键设置脚本 ├── run-test.sh # 一键启动测试推荐 ├── start-server.sh # 启动本地更新服务器 ├── stop-server.sh # 停止本地更新服务器 ├── generate-manifest.sh # 生成 manifest 和目录结构 ├── dev-app-update.local.yml # 本地测试用的更新配置模板 └── server/ # 本地服务器文件目录 (自动生成) ├── stable/ # stable 渠道 │ ├── stable-mac.yml │ └── {version}/ │ ├── xxx.dmg │ └── xxx.zip ├── nightly/ # nightly 渠道 │ ├── nightly-mac.yml │ └── {version}/ └── canary/ # canary 渠道 ├── canary-mac.yml └── {version}/三个渠道目录内{version}/存放的是 DMG/ZIP 安装包与 manifest 引用文件的宿主manifest 中files[].url与path均指向{version}/xxx.dmg、{version}/xxx.zip这样的相对地址因此把server/整体交给任意静态文件服务器即可完成分发。快速开始以下命令均在仓库根目录执行。若目录结构未初始化先进入目标目录并赋予脚本可执行权限cd apps/desktop/scripts/update-test chmod x *.sh一键测试推荐cd apps/desktop/scripts/update-test ./run-test.sh阅读 run-test.sh 源码可见它按固定顺序自动完成为三渠道生成不同版本号的 manifest → 启动本地服务器 → 把dev-app-update.local.yml复制为apps/desktop/dev-app-update.yml完成应用配置 → 检查 macOS Gatekeeper 状态 → 询问是否启动打包后的应用。整个过程中无需手工干预生成与配置环节。手动步骤适合按需拆分执行1. 首次设置cd apps/desktop/scripts/update-test ./setup.shsetup.sh 会创建三渠道目录、写入三个99.0.0占位 manifest、并基于模板生成 dev-app-update.local.yml。该模板是 electron-updater 开发态配置provider: generic url: http://localhost:8787/stable updaterCacheDirName: lobehub-desktop-local-test channel: stable注意模板内的注释点明了它的边界此文件只负责应用初始启动时的 provider 配置运行时的 channel 切换走的是UPDATE_SERVER_URL环境变量 setFeedURL()这条链路并不依赖此文件。2. 构建测试包cd apps/desktop # 构建 DMG ZIP (macOS 自动更新需要 ZIP) bun run package:mac:local注意: 不要使用package:local。从 apps/desktop/package.json 的脚本定义可以看出区别package:local走electron-builder --dir只输出目录结构不产出可被更新的 DMG/ZIP 安装包而package:mac:local走electron-builder --mac真正产出 DMG。此外package:mac:local内部注入了UPDATE_CHANNELnightly并关闭公证与签名--c.mac.notarizefalse -c.mac.identitynull这一点与后文 macOS 签名验证的边界直接相关。3. 生成更新文件cd apps/desktop/scripts/update-test # 为所有渠道生成推荐会自动分配不同版本号 ./generate-manifest.sh --from-release --all-channels # 或指定单个渠道 ./generate-manifest.sh --from-release -c nightly -v 2.1.0-nightly.1--from-release模式下脚本会从apps/desktop/release/自动探测第一个*.dmg与*-mac.zip兼容*.zip命名并尝试从 DMG 文件名中正则提取版本号先匹配x.y.z-(alpha|beta|rc|nightly|canary).n形式再退而匹配纯x.y.z。4. 启动本地服务器./start-server.sh # 服务器默认在 http://localhost:8787 启动start-server.sh 的实质是后台拉起静态服务器cd $SERVER_DIR nohup npx serve -p $PORT --cors -n $LOG_FILE 21 其中--cors保证渲染进程/更新模块的跨源请求可用-n关闭自动列出目录启动成功后 PID 记录在.server.pid日志在.server.log。端口可通过PORT环境变量覆盖。5. 启动应用开发模式cd apps/desktop UPDATE_SERVER_URLhttp://localhost:8787 bun run dev重要: 必须设置UPDATE_SERVER_URL环境变量否则 channel 切换时configureUpdateProvider()会回退到 GitHub原因见上文源码分析。UpdaterCtr / UpdaterManager 在isDev或FORCE_DEV_UPDATE_CONFIG为真时还会开启autoUpdater.forceDevUpdateConfig true从而强制加载仓库根目录的 dev-app-update.yml本地测试场景中它由脚本复制自dev-app-update.local.yml。需要说明的是dev 模式下enableAppUpdate !isDev falseupdater 不会真正初始化因此这一路径主要用于验证设置页 UI、IPC 通信与日志输出完整的检查→下载链路要在打包模式下验证。若想在打包产物中强制读取本地配置可参考run-test.sh给出的启动方式FORCE_DEV_UPDATE_CONFIGtrue UPDATE_SERVER_URLhttp://localhost:8787 open .../LobeHub.app。6. 测试 Channel 切换进入设置 Beta在Update Channel下拉框中选择不同渠道切换后应用会自动检查对应渠道的更新查看日志确认 feed URL 切换正确tail -f ~/Library/Logs/lobehub-desktop-dev/main.log打包模式日志路径为~/Library/Logs/lobehub-desktop/main.log。用 grep 过滤可关注的关键日志包括tail -f ~/Library/Logs/lobehub-desktop-dev/main.log | grep -E Switching|Configuring|channel|checkingChannel 切换:Switching update channel: stable - canaryFeed URL 切换:Configuring generic provider for canary channelManifest 匹配:Channel set to: canary (will look for canary-mac.yml)更新检测:Update available: x.y.z或Update not available切换的即时生效逻辑可回到源码印证switchChannel()通过自增的checkGeneration使在途检查失效isStaleCheck()会丢弃旧代结果若当前无检查在跑则立即checkForUpdates()触发一次新检查。7. 测试完成后cd apps/desktop/scripts/update-test ./stop-server.sh # 恢复默认的 dev-app-update.yml可选 cd apps/desktop git checkout dev-app-update.ymlgenerate-manifest.sh 用法详解generate-manifest.sh 负责产出形如stable-mac.yml的更新清单。完整参数如下用法: ./generate-manifest.sh [选项] 选项: -v, --version VERSION 指定版本号 (例如: 2.0.1) -c, --channel CHANNEL 指定渠道 (stable|nightly|canary, 默认: stable) -a, --all-channels 为所有渠道生成 manifest (stable/nightly/canary) -d, --dmg FILE 指定 DMG 文件名 -z, --zip FILE 指定 ZIP 文件名 -n, --notes TEXT 指定 release notes -f, --from-release 从 release 目录自动复制文件 -h, --help 显示帮助信息 示例: ./generate-manifest.sh --from-release --all-channels ./generate-manifest.sh -v 2.0.1 -c stable --from-release ./generate-manifest.sh -v 2.1.0-nightly.1 -c nightly --from-release生成逻辑与补充细节SHA512 计算对真实文件执行shasum -a 512 ... | xxd -r -p | base64即 electron-updater 期望的 base64 编码哈希文件不存在时写入placeholder占位便于在无构建产物时先行验证 manifest 结构。releaseDate自动取当前 UTC 时间格式化为%Y-%m-%dT%H:%M:%S.000Z。--all-channels版本编排以基础版本为锚点——stable 用基础版本nightly 取基础版本 patch1 并追加-nightly.yyyyMMddcanary 取 patch1 并追加-canary.1。三者随附的 releaseNotes 会内置测试要点提示如切回 stable 应触发降级、allowDowngrade自动置 true 等方便对照结果。生成时机建议先构建package:mac:local再--from-release能拿到带真实哈希与文件大小的 manifest只做 UI/IPC 冒烟时可先跑setup.sh生成99.0.0占位版本均高于任何本地真实版本保证有新版本可用场景成立。生成的 manifest 结构以 stable 为例形如version: 2.0.1 files: - url: 2.0.1/LobeHub-2.0.1-arm64.dmg sha512: base64 sha512 size: 123456789 path: 2.0.1/LobeHub-2.0.1-arm64.dmg sha512: base64 sha512 releaseDate: 2026-01-15T10:00:00.000Z releaseNotes: | ## v2.0.1 (Stable) ...覆盖的测试场景矩阵场景操作有新版本可用manifest 中version大于当前应用版本无新版本version小于或等于当前版本Channel 切换升级从 Stable 切到 Nightly/Canary应检测到更高版本Channel 切换降级从 Canary 切到 StableallowDowngrade应自动设为 true下载失败删除server/{channel}/{version}/中的 DMG 文件网络错误停止本地服务器Manifest 不存在删除对应的{channel}-mac.yml从源码层面manifest 不存在这类异常其实有专门的容错路径UpdaterManager 的isMissingUpdateManifestError()会识别cannot find ... 404 ... {channel}.yml形态的错误并将其按暂无更新处理setStage(latest)而不是弹错误框——本地测试时可以先删除某个渠道的 manifest 观察这种优雅降级行为。删除 DMG 文件的下载失败场景则可验证error事件分支日志会输出错误上下文channel、currentChannel、UPDATE_SERVER_URL等5 秒后 stage 回落到 idle。关于 macOS 签名验证Gatekeeper本地测试的包未经签名和公证macOS 会阻止运行。解决方法# 临时禁用 Gatekeeper推荐测试完成后务必重新启用 sudo spctl --master-disable # 测试完成后 sudo spctl --master-enable或手动移除隔离属性xattr -cr /path/to/YourApp.apprun-test.sh在打包模式启动前会自动探测 Gatekeeper 状态spctl --status若为 enabled 会给出警告并交互式确认避免用户被无法打开卡住。Squirrel.Mac 更新安装限制本地未签名构建无法完成更新的安装步骤。这是由 macOS 更新组件 Squirrel.Mac 的校验机制决定的它要求更新包的签名与当前运行 app 的 designated requirementDR匹配而 ad-hoc 签名的 DR 中包含cdhash二进制哈希不同构建的哈希必然不同因此校验必定失败。由此可以明确本地测试的边界能验证到下载完成为止检测更新、切换 feed、下载包体含下载进度广播都可以完整走通无法验证安装与重启这一步依赖真实 Apple Developer 证书仅在 CI 或具备证书的机器上存在有效签名不存在此问题。可验证的部分通过日志在上文测试 Channel 切换一节已列出覆盖 channel 切换、feed URL 切换、manifest 匹配与更新可用性判定。故障排除1. Channel 切换后仍请求旧渠道确认启动应用时设置了UPDATE_SERVER_URLhttp://localhost:8787未设置会回退 GitHub provider查看日志确认configureUpdateProvider被调用grep Configuring generic ~/Library/Logs/lobehub-desktop-dev/main.log2. 更新检测不到确认对应渠道的 manifest 存在curl http://localhost:8787/stable/stable-mac.yml确认 manifest 中的版本号大于当前应用版本结合 configs.ts 中UPDATE_CHANNEL的归一化规则只有canary/beta会被判定为 canary其余归为 stable核对当前渠道是否与 manifest 一致。3. 服务器启动失败# 检查端口是否被占用 lsof -i :8787 # 使用其他端口start-server.sh 与 run-test.sh 均读取 PORT 环境变量 PORT9000 ./start-server.sh若残留旧进程可先./stop-server.sh其实现会读.server.pid先kill再兜底kill -9最后清理 PID 文件。注意事项⚠️安全提醒测试完成后务必重新启用 Gatekeepersudo spctl --master-enable避免系统持续处于降低防护的状态这些脚本仅用于本地开发测试切勿在生产或共享环境沿用其中的占位 manifest 与未签名产物不要将未签名的包分发给其他用户——它既无法通过 Gatekeeper也无法被 Squirrel.Mac 正常安装只会制造困惑。小结测试链路与源码的对应关系测试目标操作入口对应源码位置初始 feed 指向本地UPDATE_SERVER_URLdev-app-update.local.ymlconfigs.ts、UpdaterManager.configureUpdateProvider渠道升级检测设置 Beta 切换 --all-channels高版本 manifestswitchChannel、allowPrerelease渠道降级回滚canary → stable版本号回落allowDowngrade true切换后重设manifest 缺失/网络错误删 manifest /stop-server.shisMissingUpdateManifestError容错分支借助这套脚本开发者可以像 CI 一样在提交前快速回归更新模块的核心行为而无需触碰真实发布服务器理解 manifest 与 provider 的绑定关系后也可以将其平移到非 macOS 或私有对象存储的测试场景中复用。【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考