3. 固件版本控制与分支策略
固件项目的版本控制比纯软件项目更复杂:代码要同时管理自研逻辑、芯片厂商 SDK、第三方协议栈、二进制库和构建脚本,还要让“某块板子上烧的到底是哪份代码”可追溯。本节围绕 Git 工作流、子模块与供应商代码管理、版本号规范、安全分支模型四件事展开,目标是建立一套既能支撑日常开发、又能支撑安全发布与应急响应的版本控制体系。
一、Git 工作流:从主干开发到发布分支
固件团队常见两种工作流:主干开发(Trunk-Based)和 Git Flow。对安全 CI/CD 而言,更推荐“短生命周期特性分支 + 受保护主干 + 发布分支”的混合模式。主干 main 始终保持可构建、可签名、可发布;每个特性或修复从主干拉出短分支,命名建议 feat/<issue>-<desc>、fix/<issue>-<desc>、sec/<cve>-<desc>,生命周期控制在数天内,通过合并请求(MR)合入。
发布时从主干切出 release/v1.4 分支,只接受缺陷修复和安全补丁,不再合入新功能。每个正式版本在该分支上打标签,例如 v1.4.2。这样做的关键收益是:安全修复可以精准回移到仍在维护的旧发布分支,而不必把主干上的新功能一起带过去。
main ──●──●──●──────────●──────────●──▶
\ \ ↑ ↑
feat/a ●────● │ │
\ │ │
feat/b ●──●──● │ │
\ │ │
release/v1.4 ●───●──●──●───● (仅修复)
↑ ↑
v1.4.1 v1.4.2
分支保护规则应至少包含:禁止直接推送 main 与 release/*;合并必须经过至少一名评审;CI 流水线(编译、静态检查、单元测试、签名校验)必须通过;提交必须签名(git commit -S),确保提交来源可验证。
二、子模块与供应商代码管理
固件仓库常需要引入芯片厂商 SDK、RTOS、加密库等外部代码。三种主流做法各有取舍:
- Git Submodule:主仓库只记录子模块的提交哈希,供应商代码独立仓库管理。优点是版本锁定精确、可追溯;缺点是克隆需
--recurse-submodules,更新流程繁琐,容易忘记同步。 - Git Subtree:把供应商代码合并进主仓库,克隆简单,但历史膨胀、上游同步需额外命令。
- Vendor 目录 + 清单:把供应商代码放入
vendor/,用manifest.yml记录来源 URL、版本、哈希与许可证,配合脚本拉取。适合需要审计与 SBOM 生成的场景。
无论哪种方式,都必须做到“供应商代码不可静默变更”。建议在 CI 中加入校验:对 vendor/ 与子模块目录计算哈希并与清单比对,一旦不一致即失败。同时记录每个组件的许可证与已知 CVE,为后续 SBOM 与漏洞扫描提供输入。
# 子模块方式锁定供应商版本
git submodule add https://git.vendor.com/sdk.git vendor/sdk
git -C vendor/sdk checkout 3f9a1c2
git commit -m "chore: pin vendor sdk to 3f9a1c2"
# 清单方式记录来源
# manifest.yml
# - name: sdk
# url: https://git.vendor.com/sdk.git
# commit: 3f9a1c2
# license: Apache-2.0
三、固件版本号规范
固件版本号建议采用语义化版本 MAJOR.MINOR.PATCH,并附加构建元数据。约定如下:MAJOR 变更表示不兼容的接口或硬件适配变化;MINOR 表示向后兼容的功能新增;PATCH 表示向后兼容的缺陷或安全修复。构建元数据可包含构建号、Git 短哈希与是否含未提交改动,例如 v1.4.2+build.318.g3f9a1c2。
版本号不应只存在于 Git 标签,还应编译进固件镜像,便于设备上报与现场核对。常见做法是通过构建脚本把版本写入头文件:
# 由 CI 注入,禁止手工修改
FW_VERSION_MAJOR=1
FW_VERSION_MINOR=4
FW_VERSION_PATCH=2
FW_BUILD_ID=318
FW_GIT_SHA=3f9a1c2
FW_DIRTY=0
判断依据很直接:设备启动日志或 fw_version 接口输出的版本,必须能与 Git 标签、CI 构建记录三者对齐。若出现 FW_DIRTY=1,说明构建时工作区有未提交改动,该产物不得用于正式发布。
四、安全分支模型
安全分支模型的核心是“长期维护分支 + 安全专用通道”。建议维护 main、若干 release/* 长期分支,以及一条 security/* 应急通道。当收到漏洞报告或 CVE 时,从受影响的发布分支拉出 sec/<cve>-<desc>,修复后先合入该发布分支并打补丁标签,再向上回移到 main,避免修复只停留在旧分支。
下图给出本节的核心结构:主干、发布分支、安全通道与供应商代码之间的版本锁定与回移关系。
落地时还需注意两点。其一,安全修复必须同时更新受影响的所有发布分支,建议用脚本或 CI 任务自动检测“哪些 release 分支仍包含该漏洞组件”,避免遗漏。其二,分支模型要与制品仓库联动:每个发布标签对应一份签名固件、一份 SBOM、一份构建日志,三者通过版本号与 Git 哈希互相索引。这样当现场设备上报 v1.4.2+build.318.g3f9a1c2 时,团队能立即定位到确切的源码提交、供应商组件版本与已知漏洞状态,为后续应急响应提供可靠起点。