Skip to content

统一官方站 Manifest 与全渠道检查更新架构 #296

Description

@utopiafar

背景

目前 App 的检查更新能力仍然是 Android Early 专用链路:

  • AppUpdateService.isSupported 只支持 Android && Early channel
  • 设置页里的更新入口只在 Platform.isAndroid && AppFlavor.isEarly 时展示。
  • 启动自动检查仍走 _scheduleEarlyUpdateCheck()
  • 版本来源已经具备基础能力:package_info_plus 可以读取 versionName/buildNumberAppFlavor 已经区分 global/cn/globalEarly/cnEarly/globalDev/cnDev

我们现在要把检查更新升级为全渠道统一能力,覆盖:

  • iOS 国区 App Store
  • iOS 外区 App Store
  • Android 国区应用商店
  • Google Play / 海外 Android 商店
  • GitHub / 官网直装 APK
  • Early / Dev 包

用户侧也需要一个统一的 关于 Memex / About 页面,用来展示 App logo、当前版本、渠道、安装来源、检查更新入口和复制诊断信息。

方案结论

采用 官方站静态 Manifest 作为 canonical 策略源,GitHub Releases 作为 APK 资产源与镜像/fallback

推荐主地址:

https://www.memexlab.ai/updates/v1/manifest.json
https://www.memexlab.ai/updates/v1/manifest.sha256

对应放在 memex_home

public/updates/v1/manifest.json
public/updates/v1/manifest.sha256

注意:不要使用 /v1/... 路径,因为 memex_home 当前会把 /v1/:path* rewrite 到外部 gateway。

GitHub Releases 继续负责:

  • Early/Dev/direct APK release asset
  • release notes
  • manifest 镜像/fallback
  • 人工审计和回溯

App 客户端不应把 GitHub API 作为全渠道主策略入口,避免国内网络、API rate limit、无认证 token 等问题。

改造范围

1. memex App

新增/重构:

  • AppInfoService

    • 读取 appName/packageName/versionName/buildNumber
    • 读取 AppFlavor.name/channel/region
    • Android 通过 MethodChannel 读取安装来源(installer source)。
    • 提供复制诊断信息需要的结构化模型。
  • UpdateManifestService

    • 拉取官方站 manifest。
    • 支持 GitHub fallback endpoint。
    • 校验 schema version、必要字段、渠道匹配、buildNumber 类型。
    • 可选校验 manifest.sha256;后续可升级为签名校验。
    • 网络失败时降级到内置 fallback manifest 或本地 no-op。
  • AppUpdateRouter / provider 架构

    • 根据 AppBuildInfo + manifest + installerSource 选择 provider。
    • 不让 UI 直接判断 GitHub/App Store/商店逻辑。
  • Providers

    • AppleAppStoreUpdateProvider
    • AndroidStoreRedirectProvider
    • GooglePlayUpdateProvider(可先跳转 Play Store,深度 Play Core 后续补)
    • GithubApkUpdateProvider(复用现有 APK 下载/安装能力)
    • NoopUpdateProvider
  • About 页

    • 设置页新增 关于 Memex 入口。
    • 展示 logo、版本、build、渠道、安装来源。
    • 提供“检查更新”和“复制版本信息”。
    • 将 Early 更新卡片能力迁移/收口到 About 页,而不是继续散在设置根页。
  • 启动自动检查

    • _scheduleEarlyUpdateCheck() 泛化为 _scheduleAppUpdateCheck()
    • Stable 只做弱提示/商店跳转。
    • GitHub/direct/Early 才允许 APK 下载。
    • Dev 默认关闭或仅手动检查。

2. memex_home

  • 新增静态 manifest 路径:public/updates/v1/manifest.json
  • 新增 manifest hash 文件:public/updates/v1/manifest.sha256
  • 新增 schema 文档或校验脚本,避免手工改坏。
  • 明确缓存策略:manifest 需要短缓存,APK/图片等资产可以长缓存。
  • 后续可把 /updates/* 接到 CDN/OSS,但 first pass 不需要后端。

3. 发布流程 / CI

  • 打包时生成:
    • versionName
    • buildNumber
    • flavor/channel
    • APK sha256
    • APK sizeBytes
  • 发布时更新 manifest 并校验 schema。
  • GitHub Release 上传 APK 和可选 manifest mirror。
  • 失败时阻断发布,避免客户端读取半成品 manifest。

Manifest 建议结构

{
  "schemaVersion": 1,
  "generatedAt": "2026-06-16T00:00:00Z",
  "channels": {
    "ios_cn_stable": {
      "provider": "app_store",
      "bundleId": "com.memexlab.memex.cn",
      "country": "cn",
      "latestBuild": 118,
      "minSupportedBuild": 117,
      "storeUrl": "https://apps.apple.com/cn/app/..."
    },
    "android_cn_stable": {
      "provider": "android_store",
      "packageName": "com.memexlab.memex.cn",
      "latestBuild": 118,
      "minSupportedBuild": 117,
      "fallbackStoreUrl": "market://details?id=com.memexlab.memex.cn"
    },
    "android_global_early": {
      "provider": "github_apk",
      "packageName": "com.memexlab.memex.early",
      "latestBuild": 118,
      "minSupportedBuild": 117,
      "versionName": "1.0.35",
      "apkUrl": "https://github.com/memex-lab/memex/releases/download/.../memex_globalEarly_1.0.35_118.apk",
      "sha256": "...",
      "sizeBytes": 123456789,
      "releaseNotesUrl": "https://github.com/memex-lab/memex/releases/tag/..."
    }
  }
}

重点风险盘点

1. Manifest 可用性风险

风险:官方站不可达、CDN 异常、DNS 污染、客户端网络失败,导致检查更新不可用。

规避

  • App 内置 fallback manifest。
  • 官方站失败后尝试 GitHub mirror。
  • 所有失败都降级为“无法检查更新”,不能阻塞 App 主流程。
  • About 页仍显示本地版本信息。

2. Manifest 缓存/回滚风险

风险:CDN 缓存过久导致错误 manifest 无法快速修正;或者用户拿到半旧半新的更新策略。

规避

  • manifest 使用短缓存。
  • APK 使用带版本号的不可变 URL。
  • manifest schema 增加 generatedAt 和可选 expiresAt
  • 发布流程必须支持回滚到上一份 manifest。

3. Manifest 被篡改风险

风险:攻击者篡改 manifest,把 direct APK 指向恶意包。

规避

  • 只走 HTTPS。
  • APK 下载必须校验 sha256 和 size。
  • direct APK 安装前校验文件扩展名、大小、hash。
  • 后续升级为 manifest 签名校验(如 Ed25519 公钥内置客户端)。
  • GitHub/direct APK 不允许更新商店签名包。

4. APK 签名/渠道互刷风险

风险:GitHub APK 试图覆盖 App Store/Google Play/国内商店安装包,因签名不一致失败;更糟糕的是用户被引导到错误渠道。

规避

  • manifest 中明确 packageNamechanneldistribution
  • provider 选择必须校验当前包名和 flavor。
  • 商店来源安装包只跳商店,不走 APK 下载。
  • GitHub/direct/Early 只更新同签名 direct/Early 包。

5. 安装来源识别不准

风险:Android installer source 在某些 ROM、迁移安装、备份恢复后为空或不可靠,导致跳错商店。

规避

  • installer source 只作为 hint,不作为唯一事实。
  • 优先使用 packageName/flavor/channel。
  • manifest 提供 fallbackStoreUrl。
  • About 页展示安装来源,便于用户反馈。

6. 版本比较错误

风险versionName 带 suffix(Early/Dev/日期/hash)时字符串比较出错。

规避

  • 新旧判断统一使用整数 buildNumber
  • versionName 只用于展示。
  • manifest schema 强制 latestBuild/minSupportedBuild 为整数。
  • 单测覆盖 stable/early/dev/cn/global 的版本比较。

7. 强制更新体验风险

风险:误设 minSupportedBuild 导致用户被错误拦截;离线用户无法进入 App。

规避

  • minSupportedBuild 默认不启用硬拦截。
  • 强制更新必须有二次确认/发布 checklist。
  • 本地优先 App 不应因为检查更新失败阻断核心使用。
  • 仅对明确不可兼容版本做强拦截。

8. 启动自动检查打扰风险

风险:启动弹窗频繁、影响记录入口、降低首屏体验。

规避

  • 保留最小检查间隔(如 12 小时)。
  • Stable 只弱提示,不自动弹强对话框。
  • Early/direct 可提示下载,但仍需用户确认安装。
  • Dev 默认关闭自动检查。

9. 国内网络和 GitHub 依赖风险

风险:CN 用户访问 GitHub 慢或失败,导致更新体验差。

规避

  • CN 渠道默认使用官方站/国内 CDN manifest。
  • APK 如需 direct 下载,优先国内对象存储/CDN。
  • GitHub 只作为 mirror/fallback,不是 CN 主路径。

10. Store policy 风险

风险:iOS 或应用商店不允许 App 内自行下载安装更新包。

规避

  • iOS 只检查和跳 App Store,不下载 IPA。
  • 商店安装的 Android 稳定包只跳对应商店。
  • APK 下载/系统安装器仅用于 GitHub/direct/Early 包。

11. 并发和缓存清理风险

风险:用户重复点击下载、清缓存与下载冲突、半包被复用、安装并发。

规避

  • 保留现有 AppUpdateService 的 service 层并发 ownership。
  • 下载使用 .part 临时文件。
  • hash/size 校验失败删除临时文件。
  • 单测覆盖并发下载、下载中清理、缺失 APK 安装拒绝。

12. UI 信息暴露/诊断风险

风险:About 页复制诊断信息时暴露不该有的用户数据。

规避

  • 诊断信息只包含 app/build/channel/package/installer/source/manifest timestamp。
  • 不包含 userId、路径、LLM key、日志内容。

验收标准

  • 设置页有统一 About 页入口。
  • About 页展示 logo、版本、build、channel/flavor、安装来源。
  • About 页可以手动检查更新。
  • 当前 Android Early GitHub APK 能力迁移到 provider 后保持可用。
  • iOS 渠道只提供 App Store 检查/跳转,不出现下载 IPA 行为。
  • 商店安装的 Android stable 包不走 GitHub APK 下载。
  • direct/Early APK 下载必须校验 sha256 和 size。
  • 官方站 manifest 不可达时,App 不崩溃、不阻塞主流程。
  • GitHub API 不作为全渠道默认检查入口。
  • 单测覆盖 manifest parsing、provider selection、版本比较、hash 校验、fallback。
  • Widget test 覆盖 About 页加载、检查更新状态、失败状态和复制诊断信息。

备选方案

A. 只使用 GitHub Releases / GitHub manifest

优点:最省事,发布链路简单。

缺点:客户端依赖 GitHub 网络和 API/rate limit;CN 体验差;品牌和策略入口不稳定。

结论:不推荐作为 canonical,只适合 mirror/fallback。

B. 官方站静态 manifest + GitHub fallback

优点:不需要新后端;官方域名可控;适合 CN/global 分流;GitHub 保留透明和灾备。

缺点:需要维护静态文件和发布校验。

结论:推荐一期采用。

C. 独立后端 Update API

优点:可做动态灰度、统计、分群、签名下发。

缺点:引入服务端运维、鉴权、可用性、更多故障面。

结论:当前过重,等静态 manifest 不够用再升级。

实施建议

一期直接做完整架构,但控制商店深集成范围:

  1. 先实现官方站静态 manifest、AppInfo、About 页、provider router。
  2. 商店渠道 first pass 先做检查和跳转。
  3. GitHub/direct/Early 保留 APK 下载和安装能力,并补 hash 校验。
  4. Google Play Core、华为 AppGallery SDK 等深集成后续按真实上架渠道分 issue 推进。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions