结论:设备 CI 的保护不是一道门,而是四道:设备锁、凭据注入、检出隔离与测试跳过判定。真正的风险不在「CI 通过」,而在两处静态缺口——差异列表用子串匹配判断是否跳过步骤,且检出快路径会保留被忽略文件与旧构建产物。UNSAFE=1 只是检出快路径的环境变量,不是车辆 safety 许可。
阶段骨架:node 到 deviceStage
流水线从 node 开始:setupCredentials() 注入三类 token,checkout(scm) 取分支/提交,再按分支选择 staging/nightly 或常规矩阵。特殊 chestnut-stresstest 最多循环 100 次并归档报告;其余按分支触发 staging/nightly,未排除分支运行常规并行矩阵。整个流水线的控制权都集中在 Jenkinsfile 的 node 段,因此插件化改造的关键不是换脚本,而是把设备锁、凭据与步骤过滤各自契约化。
| 函数/阶段 | 主要实现契约 |
|---|---|
setupCredentials() |
从 Jenkins 凭据读三类 token 并放入 env;后续 device() 把它们扩展到远端 shell 环境。SSH 私钥另在 withCredentials 中取得。 |
device() |
用 alpine-ssh 包装的远端 SSH;连接禁用严格主机密钥检查。AGNOS 上清临时目录/共享内存、必要时启动 DNS 与重绑 USB hub;链接 Python 路径,执行步骤并等待子进程退出。 |
deviceStage() |
禁止 Jenkins Replay;按设备标签 lock(quantity: 1) 独占,在 Docker 中给整个阶段 35 分钟;设备时间同步与检出一起最多重试三次,测试步骤本身不自动重试。 |
hasPathChanged() |
对 GitHub compare 的返回字符串做子串匹配,不是解析已验证的文件列表;非 master/非 Jenkins loop 分支可据 diffPaths 跳过部分 build/test。 |
chestnutStage() |
build、Chestnut 模型回放和指定 TestChestnutOnroad;设备标签为 mici-chestnut-ci。 |
devel-staging 调用设备上的 build_release.sh 推 tizi/mici staging;__nightly 同时提交普通与 PANDA_DEBUG_BUILD=1 变体;__nightly-chestnut 加入 INCLUDE_BIG_MODEL=1。普通矩阵并行覆盖 onroad、功耗/编码器/manager、两种 camerad、sensord、模型回放、panda loopback/SPI/功放及 Chestnut。
凭据与 SSH 信任边界
setupCredentials() 从 Jenkins 凭据读 Azure、CI 工件、GitHub 评论 token 并放入 env,随后 device() 把它们扩展到远端 shell 环境;SSH 私钥另在 withCredentials 中取得。远端连接禁用严格主机密钥检查,因此 CI 网络、节点与凭据范围应被视为独立信任边界。
需要分清的是:lock(label=...) 是 Jenkins 设备池的并发协调,不是车端进程互斥。在自建硬件平台上应收紧主机验证并落实凭据最小权限,而不是把 CI 的协调机制误当作运行时的排他保证。
从信任边界看,凭据实际有三层:Jenkins 环境中的凭据存储、经 device() 扩展到远端 shell 的环境变量,以及独立取得的 SSH 私钥。三层的作用域应各自最小化,远端主机验证应收紧;本地 ZIP 归档无法代表真实 Jenkins 插件及凭据配置,因此本文的结论只到脚本与流水线文本层。
检出两条路径:unsafe 与 safe
setup_device_ci.sh 先要求 SOURCE_DIR、GIT_COMMIT、TEST_DIR,清数据与 staging 目录,再写 /data/continue.sh。该脚本中的 continue.sh 内容含 abctl --set_success、sshd/NetworkManager 修改与长循环;此处只写文件并设可执行位,后续由什么启动机制执行应另行验证,不能把写入等同于本轮已执行。
| 路径 | 实现 | 结果/代价 |
|---|---|---|
快路径 unsafe_checkout() |
直接在 TEST_DIR fetch/强制 checkout/reset,清理,对子模块做 reset/clean/update。 |
不使用忽略文件清理标志,忽略文件和旧构建产物可留存;失败后回退 safe。 |
全清路径 safe_checkout() |
在 SOURCE_DIR fetch 固定提交,清顶层内容、全量清理与子模块,拉 LFS,再 rsync -a --delete 到 TEST_DIR。 |
删未跟踪/忽略文件和目标旧内容,隔离更强但仍依赖 Git 远端、LFS 与 rsync 成功。 |
LFS pull_lfs() |
CHESTNUT 时拉全部;普通设备排除 big driving 模型,必要时恢复指针并删对应本地 LFS 对象。 | 防止普通设备携带大模型实体;只代表该脚本处理的路径,不证明所有模型/构建输入完整。 |
| 包导入路径 | 在 TEST_DIR 创建 msgq/opendbc/rednose/teleoprtc/tinygrad 包链接,与运行时 Python 导入结构对齐。 |
链接结构需与运行时一致,否则导入行为分叉。 |
safe_checkout() 在脚本中会删除工作目录内容、源仓锁文件与设备真实路段缓存;这只能在专用 CI 设备上使用,不能作为用户车辆的插件更新机制。
跳过与漏测风险:hasPathChanged 的子串匹配
deviceStage() 用 curl -s ... | jq ... || echo "/" 取得差异;未使用失败即错选项、管道错误传播或结构化校验。API/解析失败可能返回仅 /,后续 hasPathChanged() 找不到目标路径,导致带 diffPaths 的步骤被跳过。这是代码路径风险,不表示当前 CI 已发生漏测。
跳过的范围也需准确理解:diff 是与 master 比较后的文本,跳过仅应用于带 diffPaths 的步骤且非 master/非 Jenkins loop,不是整个矩阵的开关。模型回放路径的 build 和 replay 都带同一过滤条件,其他硬件测试大多总跑。
CI 绿灯不等于实车授权
Chestnut 压测仅对特定中断异常记 FAIL 并继续,其它异常会使循环中断,但 finally 仍归档包含 INCOMPLETE 的报告;常规分支的测试集合还受设备资源、差异跳过和环境变量影响。因此 CI 绿灯不能替代完整覆盖,也不能作为实车安全性的证明。本专题未运行 Jenkins Groovy、compare API、SSH、设备锁、模型回放或硬件测试。
换句话说,四道门保护的是「谁来跑、在哪跑、跑什么、跑哪些」这四件事,它们各自都有可被绕过的边界;把它们理解为一条不可绕过的单一闸门,正是设备 CI 最常见的误读。
常见问题
为什么差异接口失败可能导致步骤被跳过?
因为 deviceStage() 用 curl -s ... | jq ... || echo "/" 取得差异,未使用失败即错、管道错误传播或结构化校验。API/解析失败可能返回仅 /,hasPathChanged() 找不到目标路径,带 diffPaths 的步骤就会被跳过。
省略的检出快路径有什么代价?
unsafe_checkout() 不使用忽略文件清理标志,忽略文件和旧构建产物可留存,因此隔离更弱;失败后才回退到 safe_checkout()。这条快路径仅适用于专用 CI 设备。
lock 是否等同于车端进程互斥?
不等同。lock(label=...) 是 Jenkins 设备池的并发协调,不是车端进程互斥。CI 网络、节点与凭据范围应被视为独立信任边界,并在自建硬件平台收紧主机验证与凭据最小权限。
CI 全绿是否代表实车安全可用?
不代表。Chestnut 压测仅对特定中断异常记 FAIL 并继续,其它异常会中断循环但仍归档 INCOMPLETE 报告;常规测试集合还受设备资源、差异跳过和环境变量影响,CI 绿灯不能替代完整覆盖。
延伸阅读
- 测试运行器与并行夹具契约 —— 承接 CI 之外的本地测试编排边界。
- 进程回放验证 —— 说明模型回放路径在 CI 中的角色与前提。
- 安全测试夹具与回放证据 —— 对照硬件测试为何需要额外证据。
- 设备身份与注册 —— 承接设备锁与凭据绑定的身份侧边界。
- Ed25519 签名加 96 字节事务记录:让 MCU 升级不怕中途断电 —— 对照替代 CI 直刷的签名发布路径。
参考资料
- 项目 02 ——
openpilot-research/JENKINS_DEVICE_CI_CHECKOUT_LOCK_CREDENTIAL_TEST_GATE_DEEP_DIVE.md(取用了阶段骨架、凭据与 SSH 边界、检出两路径及跳过与信任边界)。 - 项目 02 ——
openpilot-research/RELEASE_BUILD_FILE_SELECTION_LFS_PUSH_CONTRACT_DEEP_DIVE.md(仅用于确认发布构建与强推语义的边界)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11