Folo OTA 发布后如何验证移动端更新是否生效
【免费下载链接】follow🧡 Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow
在 Folo 中完成一次移动端 OTA 发布(向 GitHub Release 上传ota-release.json与dist.tar.zst,并触发 Worker 同步)之后,不能直接认为更新已上线。你需要逐项确认:apps/otaCloudflare Worker 已完成同步、/manifest在 iOS 和 Android 两套请求头下都能解析到新的releaseVersion、返回的 launch asset 可下载、/policy返回预期动作,最后才是移动端应用真正拿到更新。本文按 OTA Service Operations 这份运行手册整理发布后的完整验证路径。整个链路中 GitHub Releases 是发布源(source of truth),Cloudflare Worker、KV 和 R2 是投递层,验证命令从apps/ota目录或仓库根目录执行。
验证前的两项前置确认
在跑任何 curl 之前,先确认发布物料本身是完整的,因为 Worker 的同步逻辑只会收录同时包含ota-release.json和dist.tar.zst的 release,缺任一项,新releaseVersion就永远不会被 manifest 解析出来:
- 目标 release 的 Git tag 存在,例如
mobile/v0.4.2; - 对应的 GitHub Release 同时包含
ota-release.json和dist.tar.zst。
publish-ota workflow 在发布前会检查 tag 是否存在(mobile/v${{ inputs.release_version }}),并在上传资产后自动调用 trigger-ota-sync.mjs 触发同步。即使 workflow 已经跑过,runbook 仍要求发布后手动执行一遍下面的验证序列。
设置验证环境变量
runbook 要求先设置以下环境变量,再执行手动检查。下面的值是文档示例(0.4.2/0.4.1),执行前替换成你当前要验证的 release 版本号;OTA_SYNC_TOKEN是密钥,按部署环境配置,不在仓库中。
export OTA_BASE_URL="https://ota.folo.is" export OTA_PRODUCT="mobile" export OTA_PLATFORM="ios" export OTA_CHANNEL="production" export OTA_RUNTIME_VERSION="0.4.1" export OTA_INSTALLED_BINARY_VERSION="0.4.1" export OTA_DESKTOP_PLATFORM="desktop/windows/exe" export OTA_RELEASE_VERSION="0.4.2" export OTA_SYNC_TOKEN_HEADER="x-ota-sync-token" export OTA_SYNC_TOKEN="<secret>" export OTA_GOOD_RELEASE_VERSION="0.4.1"其中OTA_RUNTIME_VERSION必须等于本次 OTA release 在ota-release.json里声明的runtimeVersion(与已上架 store 二进制兼容的版本),而不是releaseVersion。OTA_RELEASE_VERSION是你期望 manifest 解析出的新版本号,用于后面比对。
触发同步并检查健康状态
在apps/ota目录下触发一次同步(脚本路径相对于apps/ota),随后检查健康端点:
OTA_BASE_URL="$OTA_BASE_URL" \ OTA_SYNC_TOKEN="$OTA_SYNC_TOKEN" \ OTA_SYNC_TOKEN_HEADER="$OTA_SYNC_TOKEN_HEADER" \ node ../../.github/scripts/trigger-ota-sync.mjs curl --fail --silent --show-error \ "$OTA_BASE_URL/internal/health"脚本会带 token 请求头 POST 到/internal/sync,默认超时 120 秒,成功后输出Triggered OTA sync successfully。该同步会同时刷新 GitHub release 元数据和 store 商店版本缓存。
健康检查的判断标准(对应 runbook 检查单第 4、5 条):
/internal/health返回的lastSuccessAt是新鲜的,说明刚才触发的同步成功写入;storeVersionLastSuccessAt也是新鲜的,说明 store 版本缓存已刷新(定时任务每 5 分钟刷新一次,手动 sync 会立刻刷新)。
如果lastSuccessAt没有更新,说明同步本身失败,应停在这里排查,而不是继续往下做 manifest 验证。
验证 /manifest 解析出新版本
/manifest是移动端 Expo Updates 请求的入口,要求带expo-platform和expo-runtime-version请求头,expo-channel-name缺省为production,product缺省为mobile。
iOS 的完整验证(请求 manifest、比对版本号、下载 launch asset):
curl --fail --silent --show-error \ -D /tmp/ota-manifest.headers \ -H "expo-platform: ios" \ -H "expo-runtime-version: $OTA_RUNTIME_VERSION" \ -H "expo-channel-name: $OTA_CHANNEL" \ "$OTA_BASE_URL/manifest?product=$OTA_PRODUCT" \ | tee /tmp/ota-manifest.json jq -r '.metadata.releaseVersion' /tmp/ota-manifest.json jq -r '.launchAsset.url' /tmp/ota-manifest.json curl --fail --silent --show-error \ "$(jq -r '.launchAsset.url' /tmp/ota-manifest.json)" \ -o /tmp/ota-launch-asset判断标准:
- 第一条
jq输出的releaseVersion必须等于你刚发布的OTA_RELEASE_VERSION(示例中即0.4.2); - 第二条输出的
launchAsset.url应是 Worker 提供的资产地址; - 第三条命令
curl --fail能完整下载说明 bundle 在 R2 中已可达。
Android 只需把平台头换成android,确认能解析出同一个版本号:
curl --fail --silent --show-error \ -H "expo-platform: android" \ -H "expo-runtime-version: $OTA_RUNTIME_VERSION" \ -H "expo-channel-name: $OTA_CHANNEL" \ "$OTA_BASE_URL/manifest?product=$OTA_PRODUCT" \ | jq -r '.metadata.releaseVersion'一个已文档化的边界行为:当不存在兼容的 OTA release 时,/manifest返回204而不是错误。所以验证时如果收到 204,说明该 release 没有被 Worker 索引,或请求的runtimeVersion/ channel 与 release 声明不匹配——先回到同步健康与ota-release.json元数据核对,而不是反复重试请求。
验证 /policy 返回预期动作
/policy独立于 manifest 解析,它根据已安装的二进制版本判断是否需要走 store 更新:
curl --fail --silent --show-error \ "$OTA_BASE_URL/policy?product=$OTA_PRODUCT&platform=$OTA_PLATFORM&channel=$OTA_CHANNEL&installedBinaryVersion=$OTA_INSTALLED_BINARY_VERSION" \ | tee /tmp/ota-policy.json jq . /tmp/ota-policy.json该端点要求platform和installedBinaryVersion,channel与product分别缺省为production和mobile,并会自动检测当前线上 store 版本(iOS 查 App Store lookup API,Android 查 Google Play storefront)。返回值为none或prompt,判断标准是它与你本次 release 在ota-release.json中声明的 policy 语义一致。
在真机上确认移动端拿到更新
服务端三项检查(health、manifest、policy)通过后,还需要确认应用端行为符合预期。移动端 OTA 模块(OtaProvider)的运行时行为是:
- 应用启动后在后台执行一次更新检查(dev 构建会跳过该后台检查);
- 检查到可用更新后静默下载,默认在下一次启动时生效,不做强制立即 reload;
- 非生产构建提供调试动作,可手动触发检查(
Updates.checkForUpdateAsync)并立即 reload 应用已下载的更新; - 应用启动时还会调用
/policy,store 强制更新场景由 policy 结果驱动提示。
因此真机侧的验证路径是:服务端检查通过后,重启目标客户端让它在下次启动应用新下载的版本;如果手头是非生产(preview 渠道)构建,可以直接用调试菜单里的手动检查与立即 reload 动作来加速确认,不必等下次启动。设计文档中的端到端验证项即为:验证 iOS preview 二进制能下载该更新、验证 Android preview 二进制能下载该更新。注意客户端上报的runtimeVersion必须与 release 一致(app.config.base.ts 中updates.url指向https://ota.folo.is/manifest,runtimeVersion由OTA_RUNTIME_VERSION覆盖或包版本决定),否则 manifest 会按 204 处理,表现为"发布成功但设备收不到更新"。
收尾:运行自动化验证命令
runbook 要求关闭发布前执行一组自动化命令(从仓库根目录运行),覆盖 OTA Worker 与移动端 OTA 模块的测试和类型检查:
pnpm --filter @follow/ota test pnpm --filter @follow/ota typecheck pnpm --filter @follow/mobile exec vitest run src/modules/ota/__tests__/client.test.ts src/modules/ota/__tests__/store.test.ts src/modules/ota/__tests__/provider.test.ts pnpm --filter @follow/mobile typecheck pnpm exec prettier --check .github/workflows/publish-ota.yml .github/workflows/tag.yml .github/scripts/trigger-ota-sync.mjs .github/scripts/trigger-ota-sync.test.ts pnpm exec prettier --check .github/scripts/build-ota-release.mjs .github/scripts/build-ota-release.test.tsREADME 中同一清单还包含 desktop updater 的对应命令,属于桌面端发布验证范围,移动端场景可以跳过。
边界与限制
如果验证发现 manifest 指向了坏版本,需要知道 v1 的 rollback 能力是有限的:通过改写 KV 中latest:<product>:<channel>:<runtimeVersion>:<platform>指针可以临时回退,但 sync 任务始终会把最高兼容的releaseVersion提升上来——只要坏 release 仍作为有效发布源存在,下一次定时或手动同步就会把指针指回坏版本。所以在下次同步之前必须二选一:从 GitHub Releases 删除或作废坏 release 资产,或在同一runtimeVersion上发布一个更新的修正 OTA release。此外,hard freeze 和 disable 开关在当前已验证的 v1 Worker 中尚未实现,不要依赖"冻结"操作。更多发布与回滚细节可参考 OTA 设计文档。
【免费下载链接】follow🧡 Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考