- 移动开发
- 企业应用
【免费下载链接】thunderbird-android
Thunderbird for Android – Open Source Email App for Android (fka K-9 Mail)
本文以 docs/release/developer-checklist.md 为核心骨架,结合 docs/release/RELEASE.md 的发布流程定义与仓库内真实源码(feature flag 目录、合并脚本、构建脚本),系统讲解 Thunderbird for Android 项目中普通开发者/贡献者在发布列车(Release Train)机制下的全部职责:日常维护、软冻结期准备、合并前 Feature Flag 与翻译检查、Uplift 申请、以及合并后的回归验证。读完本文,你将掌握在
main→beta→release每次计划性合并之前,作为功能负责人应当完成的每一项检查动作及其背后的项目机制。
一、背景:Thunderbird for Android 的发布列车模型
Thunderbird for Android 采用“发布列车”(release train)模型来保证及时、可预测的发布节奏。整个模型由三条分支构成,每条分支对应不同的受众与稳定级别:
| 分支 | 用途 | 发布节奏 | 受众 | 应用 ID(TfA) |
|---|---|---|---|---|
main(Daily) | 新功能与改进的活跃开发 | 每日构建 | 开发者与高技术水平用户(不稳定,不建议生产使用) | net.thunderbird.android(daily 后缀) |
beta | 预发布测试 | 每周发布 beta 版本(可跳过);每 4 周合并一次 | 早期采用者与测试者 | net.thunderbird.android.beta |
release | 稳定版 | 大版本每 4 周;小版本在大版本 2 周后(可跳过);每 4 周合并一次 | 普通用户 | net.thunderbird.android(K-9 为com.fsck.k9) |
在合并日(Merge Day),main被合并进beta,随后beta被合并进release,从而让社区依次以“Daily → Beta for Testers → 正式版”的节奏体验新版本。开发者检查清单针对的正是这两次计划性合并:
main→betabeta→release
而完整的发布驱动流程(分支锁定、公告、发布)由发布驱动者(release driver)负责,详见 Release → Release Process。
二、日常持续职责(两次合并之间)
清单要求开发者把以下工作融入日常开发,而不是等到合并日前才临时补课。
2.1 提前识别潜在的 Uplift
- 在 issue/PR 中补充风险(risk)与用户影响(user impact)说明;
- 确保修复先落在
main上,并在 Daily 渠道充分“烘烤”(bakes)验证; - 具体 Uplift 的适用范围、判断标准与申请流程见 Uplifts 与 Uplift Criteria,本文第五节会展开。
2.2 字符串与翻译管理
- 尽量避免在临近合并时改动字符串;若无法避免,务必保持改动很小,让翻译者能及时跟上;
- 对于 Uplift,优先选择不修改任何可本地化字符串的补丁;
- 只有确有必要时才修改仓库内的翻译,遵循 Changing translations in this repository 的规范。
这一条在项目里是硬约束:RELEASE.md 的 Uplift Criteria 明确要求 uplift 的补丁不得改动任何可本地化字符串。从 docs/contributing/managing-strings.md 可以看到,项目的源字符串固定为美式英语(en),翻译全部托管在 Weblate,通过Translation - Update工作流以 PR 形式合入仓库。因此临近发布时新增字符串意味着所有语言都要重新走一遍翻译周期,这是清单反复强调“避免晚期字符串改动”的根本原因。
2.3 你负责领域的质量信号
- 持续关注自己负责模块的崩溃(crash)/ANR 报告与 GitHub issue;
- 主动调查回归(regression),不要等用户报告。
2.4 项目管理
- 保持自己的 issue 在项目中的状态最新(assignees、labels、status),并将 PR 关联到对应 issue;
- 确保 issue 被加入项目 Sprint 看板并分配到当前 Sprint;
- 定期审视看板,在容量允许时领取积压项,尤其是 bug 与回归;
- 审核外部贡献(社区 PR)时:
- 若未关联,将 issue 挂到合适的父 issue 下(例如
[EPIC] Mobile Foundations QX 20XX); - 将 issue 加入项目 Sprint 看板并分配到当前 Sprint。
- 若未关联,将 issue 挂到合适的父 issue 下(例如
三、main → beta 合并之前:软冻结期内的开发者职责
[!NOTE] 在
main合并进beta之前,会有一周的软冻结(Soft Freeze),详见 Soft Freeze。
3.1 软冻结期间的行为约束
- 不要合入有风险的代码改动;
- 不要启用当前处于关闭状态的 Feature Flag。
目标是确保main上的改动对更广泛的受众是安全的——因为合并进beta后,功能会暴露给所有 Beta 测试者。
3.2 Feature Flags 检查
- 确保标志位符合 beta 分支的规则:
- 除非明确批准用于 beta,否则新功能默认禁用;
- 未就绪的功能必须保持禁用;
- 在软冻结开始之前,准备好并合入一个修改标志位的 PR(在
main上)。
3.3 翻译检查
- 确保你功能所需的翻译更新已合入
main; - 若没有待处理的 Weblate PR,主动触发一个并帮助 review(必要时解决冲突)。
四、beta → release 合并之前:发布前开发者职责
目标是确保beta上的改动对**正式发布(General Availability)**是安全的。
- Feature Flags:核对标志位符合 release 分支的规则:
- 除非明确批准用于 release,否则功能一律禁用;
- 未就绪的功能继续保持禁用;
- 若需要变更,在
main上开 PR,并按 Uplift Criteria 申请 uplift 到beta。
- 翻译:此阶段不允许新增字符串;确认你的改动不会引入新的可本地化字符串。
- 可影响的稳定性检查:
- 复查影响 beta 与 release 的崩溃/ANR 报告与 GitHub issue;
- 调查回归,必要时提出修复方案;
- 确保你的改动已在 beta 上经过测试,并解决测试中发现的问题。
五、Feature Flag 机制深挖:清单背后的一等公民
清单反复要求开发者检查 Feature Flag,其原因是 Feature Flag 是 Thunderbird for Android 控制功能发布范围的核心机制。它的规则在 RELEASE.md 中定义如下:
- 在
main上:当开发者完成某功能相关的全部 PR 后,标志位即被启用; - 在
beta上:标志位保持启用,除非功能未完全完成、开发者希望暂停该功能; - 在
release上:标志位保持禁用,直到明确决定面向所有用户启用。
5.1 目录驱动的标志位定义
所有 Feature Flag 必须定义在 JSON 目录 config/featureflag/thunderbird_mobile_featureflag.catalog.json 的flags数组中,并使用同目录下的 JSON Schema 校验。以仓库当前目录为例:
{ "$schema": "thunderbird_mobile_featureflag.schema.json", "version": "2026-07-30.1", "flags": [ { "key": "use_compose_for_message_reader", "default": false, "description": "Renders the message reader using Jetpack Compose." }, { "key": "use_new_message_reader_css_styles", "default": true, "description": "Uses new message-reader CSS styles.", "time_to_promote": "2026-12-31" } ] }字段说明:
key(必填):标志位唯一标识,用于生成GeneratedFeatureFlagKey枚举;default(必填):默认开关状态;新功能一律以false起步;description(可选):便于维护者理解用途,会显示在调试界面;time_to_promote(可选):预期晋升时间,用于跟踪功能是否应被提升到 daily/beta/release,也用于清理死代码。
5.2 按应用与构建类型覆盖(overrides)
目录中的overrides字段可按应用(thunderbird / k9)与构建类型(debug / daily / beta / release)精确控制标志位。以当前仓库为例,display_in_app_notifications标志在不同渠道的状态为:
"overrides": { "thunderbird": { "debug": { "display_in_app_notifications": true }, "daily": { "display_in_app_notifications": true }, "beta": { "display_in_app_notifications": true }, "release": {} }, "k9": { "debug": { "display_in_app_notifications": true }, "release": {} } }这正是清单中“beta 上已批准的实验功能可以启用,release 上未批准一律禁用”规则在数据层面的直接体现——beta有该标志,而release的 override 是空的。解析顺序为:运行时覆盖值(Runtime Override)→ 目录覆盖值(Override)→ 默认值(Default)。
5.3 底层实现与代码使用
- 目录与 Schema 的校验由 build-plugin 中的
FeatureFlagRootPlugin(build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagRootPlugin.kt)在配置阶段完成,确保目录定义合法; FeatureFlagLibraryPlugin(build-plugin/plugin/src/main/kotlin/net/thunderbird/gradle/plugin/featureflag/FeatureFlagLibraryPlugin.kt)会自动生成GeneratedFeatureFlagKey枚举,开发者无需手工维护枚举类;- 运行时通过
FeatureFlagProvider注入查询,例如:
class MyViewModel : ViewModel() { private val featureFlagProvider: FeatureFlagProvider by inject() fun guardedLogic() { if (featureFlagProvider.provide(GeneratedFeatureFlagKey.USE_COMPOSE_FOR_MESSAGE_READER).isEnabled()) { // 新实现 } else { // 旧实现 } } }- 在 debug 构建中,
RuntimeDebugOverrideFeatureFlagProvider允许开发者通过“Secret Debug Screen”临时覆盖任意标志位,并通过 ConfigStore 持久化。
因此,清单中“软冻结期间不要启用关闭中的标志位”“合并前按目标分支规则核对标志位”并非空泛要求——在仓库机制里,这些标志位直接影响最终 APK 中功能是否对用户可见。
六、翻译流程深挖:避免晚期字符串改动的原因
清单对翻译的三条要求(保持改动小、Uplift 不改字符串、release 前零新增)源于项目的翻译工作流:
- 源字符串以英文存放在 Android 资源
res/values/strings.xml与 Compose Multiplatform 资源的src/commonMain/composeResources/values/strings.xml中; - 翻译统一在 Weblate 完成,再通过
Translation - Update工作流拉取并生成 PR 合入仓库; - 语言的新增/移除以 70% 翻译率(低于 60% 移除)为标准,通过 scripts/translation CLI 检查覆盖率。
因此,一个字符串从合入main到完成全部语言翻译需要完整的 Weblate 周期。如果临近main → beta合并才合入新字符串,翻译可能来不及在 beta 阶段完成;如果 Uplift 携带字符串改动,则违背了 Uplift Criteria。这也解释了为什么清单要求:“确认你的改动不会引入新的可本地化字符串”。
七、Uplift 机制:当修复必须“插队”时
清单反复提及 uplift(向上游分支提升修复),完整机制定义在 RELEASE.md。Uplift 应尽量避免,修复原则上应“随列车走”(ride the train)。只有 bug 足够严重时才走 uplift 流程。
7.1 Uplift Criteria(提升标准)
向 Beta 与 Release 的 uplift 应满足:
- 仅限稳定性、安全或高影响修复;功能必须随列车发布(features must ride the train);
- 提升到 Beta:修复必须已合入
main,并在 daily 渠道测试、稳定; - 提升到 Release:修复必须已合入
beta,并在 beta 渠道测试、稳定; - 必须有测试,或在缺少测试时提供强有力的说明;
- 不得修改任何可本地化字符串;
- 在 GitHub issue 中留言评估补丁必要性及引入该补丁的风险。
可接受的 uplift 类型包括:重大崩溃修复、高流量启动崩溃修复、安全修复、数据丢失修复、影响面大的高影响回归修复、重大功能中的高影响 bug 修复。
7.2 Uplift Process(提升流程)
- 申请者针对目标 uplift 分支创建 PR;
- 申请者在 PR 中填写并附加 Approval Request 模板注释;
- 发布驱动者审核请求:批准则合并,拒绝则关闭并附上说明。
八、PR 检查清单模板(可直接复制)
清单文档为开发者提供了一个可直接粘贴到 PR 描述中的片段,帮助 reviewers 和发布驱动者快速验证合并就绪度。建议完整保留使用:
- [ ] Feature flags set according to target branch rules ([beta](https://link.gitcode.com/i/bd4684105d361c99ebfb28c174d04473#feature-flags) / [release](https://link.gitcode.com/i/bd4684105d361c99ebfb28c174d04473#feature-flags)) - [ ] Tests added/updated; CI green on affected modules - [ ] No new localizable strings (or justified and coordinated) - [ ] Translations accounted for (Weblate PR merged or not required) - [ ] Uplift template with risk/impact notes filled out if proposing uplift ([criteria](https://link.gitcode.com/i/bd4684105d361c99ebfb28c174d04473#uplift-criteria))九、合并之后:开发者需要验证什么
合并完成并发布到对应渠道后,开发者的工作并未结束:
- 验证自己改动的结果:在发布后关注与你的改动相关的崩溃/ANR 与错误报告,确认没有引入回归;
- 准备热修复:如有必要,通过 uplift 流程准备/提出 hotfix。
[!NOTE] 合并当天的协调工作(分支锁定、Matrix 公告、脚本运行)由发布驱动者负责,详见 Merge Process。开发者无需执行这些操作。
十、配套机制:合并脚本、版本号与里程碑
虽然分支锁定、合并执行由发布驱动者完成,但了解底层机制有助于开发者理解合并日的产物(比如为什么 beta 分支上的功能开关状态、版本号会变化)。
10.1 合并由脚本驱动
合并通过 scripts/ci/merges/do_merge.sh 执行:
# 合并 main 到 beta scripts/ci/merges/do_merge.sh beta # 合并 beta 到 release scripts/ci/merges/do_merge.sh release脚本会在开始前提示确认分支锁定与 Matrix 公告等前置条件,并配置两个自定义合并驱动:
merge.ours.driver:保留目标分支独有的文件(如各分支专属的changelog_master.xml、版本号);merge.merge_gradle.driver:调用 scripts/ci/merges/merge_gradle.py 自动处理build.gradle.kts中的versionNameSuffix与versionCode。
合并过程中需要重点 review 的文件包括:
- app-k9mail/build.gradle.kts
- app-thunderbird/build.gradle.kts
app-k9mail/src/main/res/raw/changelog_master.xml(release 分支不得包含 beta 发布说明)
10.2 版本号与版本码规则
- 稳定版版本名遵循
X.Y格式:X 为大版本(每个发布周期递增),Y 为补丁版本(在既有大版本上追加改动时递增); - Beta 追加
b1后缀(数字随每个 beta 递增);Daily 追加恒定不变的a1后缀; - 版本码(versionCode):beta/release 为每个新发布递增的整数;daily 按日期计算,格式为
yDDDHHmm,其中y为自 2023 年起的年数(如 2024 年为 1),DDD为年内第几天(3 位补零),HH为小时(00–23),mm为分钟。
在 app-thunderbird/build.gradle.kts 中可以印证这些规则的实际实现:基础versionCode/versionName之上,beta 构建类型追加applicationIdSuffix = ".beta"与versionNameSuffix = "b0",daily 追加.daily与a1,debug 追加.debug与-SNAPSHOT。
10.3 里程碑(Milestones)
项目用 GitHub Milestones 跟踪每个大版本的开发,同一时刻恰好保持 3 个开放的里程碑(main、beta、release各一个目标),部分自动化依赖这一约定。日期最远的里程碑对应main分支目标,最近的对应release分支;发生 uplift 时里程碑会相应调整。
十一、总结:一张图记住开发者的发布职责
把清单浓缩为开发者在每个阶段的“三件事”:
| 阶段 | 核心动作 |
|---|---|
| 日常(两次合并之间) | 提前评估 uplift 需求、控制字符串改动、盯质量信号(crash/ANR)、维护 issue/看板 |
| 软冻结(main → beta 前一周) | 不合高风险代码、不启用关闭中的标志位、按 beta 规则核对 Feature Flags、合并翻译 |
| beta → release 前 | 按 release 规则核对 Feature Flags、零新增字符串、复查稳定性并确保 beta 实测通过 |
| 合并发布后 | 跟踪回归信号,必要时经 uplift 流程提交 hotfix |
这套检查清单的本质是把“发布质量责任”前置到每个功能负责人:Feature Flag 决定“功能是否可见”,翻译决定“多语言是否就绪”,质量信号决定“是否值得冒险”,而 Uplift 是唯一的“插队通道”,且只对稳定性/安全/高影响修复开放。开发者只要按清单逐项落实,就能确保自己的改动安全地随发布列车进入 beta 与 release,而不是在合并日成为阻塞项。
- 移动开发
- 企业应用
【免费下载链接】thunderbird-android
Thunderbird for Android – Open Source Email App for Android (fka K-9 Mail)
相关推荐
Thunderbird for Android 发布火车模型全解析:从 Daily 到 Release 的自动化发布流程
Thunderbird for Android 发布火车模型全解析:从 Daily 到 Release 的自动化发布流程 Thunderbird for And
移动开发企业应用OmniRoute 发布检查清单(Release Checklist)完全指南:从版本号到产物的全链路发布流程
OmniRoute 发布检查清单(Release Checklist)完全指南:从版本号到产物的全链路发布流程 本篇指南围绕 OmniRoute 仓库的发布检查
后端API网关LLM 网关人工智能大模型MCP 服务桌面应用OmniRoute 发布检查清单(Release Checklist):从版本号到发布工件的全流程质量门禁指南
OmniRoute 发布检查清单(Release Checklist):从版本号到发布工件的全流程质量门禁指南 本文是 OmniRoute 开源仓库发布流程的完
后端API网关LLM 网关人工智能大模型MCP 服务桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考