Safe Haven 应用版本记录和发布说明,每个版本独立的扫描历史
版本记录的结构
Safe Haven 索引(index.json)里每个应用条目有一个 versions 数组,每个版本是数组里的一个独立对象。版本记录的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| versionName | 字符串 | 显示给用户的版本号(如 "2.1.0") |
| versionCode | 整数 | 安卓内部版本号(单调递增) |
| apkPath | 字符串 | S3 存储桶里的 APK 路径 |
| fileSize | 整数 | APK 文件大小(字节) |
| sha256 | 字符串 | APK 文件的 SHA-256 哈希 |
| signedAt | Unix 时间戳 | 签名日期(如果可提取) |
| scannedAt | Unix 时间戳 | 最近一次扫描时间 |
| addedAt | Unix 时间戳 | 首次进入 Safe Haven 目录的时间 |
| releaseNotes | 字符串 / null | 开发者提供的发布说明 |
版本记录怎么进 Safe Haven
开发者提交
开发者通过 Web 管理门户提交新版本的 APK,系统自动从 manifest 里提取 versionCode 和 versionName(如果和开发者声明的不一致会自动覆盖),然后走扫描管线。通过后版本记录就加进了 versions 数组。
F-Droid / IzzyOnDroid 同步
同步时拉取上游仓库的索引,提取当前活跃版本的信息(12 个月内有更新),每个活跃版本作为一条版本记录加进来。发布说明如果上游索引里有就同步过来。
社区贡献
社区提交的应用,扫描时从 manifest 里自动提取版本号,不需要手动填。发布说明如果没有就为 null。
新版本进入后旧版本怎么办
这取决于来源:
- 开发者提交:新版本发布后,旧版本通常还保留在 versions 数组里,作为历史版本可以回滚。Safe Haven 不自动清除旧版本。
- F-Droid / Izzy 同步:上游仓库更新了版本后,Safe Haven 下次同步会更新索引,但旧版本的 APK 文件可能还在存储桶里(版本记录会保留)。
- 统一 Poller:每 6 小时一次的统一 Poller 检查上游是否有新版本,有就拉下来进扫描管线。
发布说明(Release Notes)
发布说明的来源:
- 开发者自己在提交新版本时填写
- F-Droid / Izzy 上游索引里如果有同步过来
- GitHub / GitLab Release 页面的描述(自动化发现的条目会尝试抓取)
没有发布说明的版本记录,releaseNotes 字段为 null。
v2.0.9 之前的一个更新:Safe Haven 客户端能显示跨平台可见的发布说明("What's New section for apps with visible release notes across platforms")。这意味着从客户端打开应用详情页时,如果开发者在 GitHub Release 或 Web 上提供了更新日志,客户端也能拉取展示。
版本记录里能看出什么
打开应用详情页的版本历史区,你能看到:
- 版本号是否单调递增:如果从 v1.0 直接跳到 v3.0,说明中间版本被跳过了(可能是同步时只拉了最新版)
- 扫描时间戳:每个版本独立记录扫描时间——旧版本的扫描时间可能是当时的,新版本是后来的
- 签名哈希一致性:所有版本的签名哈希应该一致(除非是合法轮换),如果某个版本哈希不同需要警惕
- 文件大小变化:大幅的文件大小变化(比如从 20MB 变到 40MB)需要关注原因
版本代码(versionCode)和版本名(versionName)
安卓系统里 versionCode 是整数(内部比较用,必须单调递增),versionName 是字符串(给用户看的版本号)。Safe Haven 扫描时会从 APK manifest 里真实提取这两个值,覆盖开发者提交时可能填错的值。
这意味着:如果开发者声称提交的是 versionCode 42 的版本,但 manifest 里实际是 41,Safe Haven 会记录 versionCode = 41,而不是开发者声明的 42。manifest 是真相源。
去重规则
Safe Haven 的提交流水线有三自动拒绝规则,任何一条触发就进 rejected 状态:
- 重复 APK SHA-256:提交的 APK 和已有的某个版本 SHA-256 完全一样 → 自动拒绝
- 重复 versionCode:提交的 versionCode 和同包名下已有的某个版本一样 → 自动拒绝
- 包名不匹配:APK manifest 里的包名和应用条目记录的包名不一样 → 自动拒绝
这些规则保证了 versions 数组里不会有重复条目。