连续几周,我的CI徽章都是红的,我已经学会视而不见。发布日那天,我终于把日志从头读到尾——红色背后藏的东西,比红色本身更糟。
当天的计划是:构建、上传、打标签、收工。结果给HivePlane v0.1.0打标签,成了整个项目里最有价值的一次调试。因为发布日是第一天,所有事情必须同时成立。如果你也曾把失败的CI徽章当成背景噪音,第3条是写给你的。如果你要发布软件包,第1条对所有人都适用。
以下是我发现的五件事,按尴尬程度从高到低排列。
一个35 MB的第三方文件,藏进了我的22 MB包里
第一次打包"成功"了。然后我列出了它的内容:1442个文件,包括整个现场测试语料库——其中有一个35 MB的JSON,是上游仓库的提交记录——外加111个来自第三方代理检出的文件。
我根本没有配置过sdist。默认设置悄悄打包了整个工作目录的一部分,而不是产品本身。我的第一个修复方案——排除列表——依然在泄漏:构建器遍历的是磁盘,不是被追踪的代码树,所以未被追踪的第三方文件从裂缝里溜了进去。
真正管用的修复是反过来的思路:允许列表。只有src/hiveplane加上根目录的元数据文件能进入sdist,其余一律默认排除。
提示:对于产物内容,允许列表胜过排除列表。排除列表列举的是你记得要害怕的东西,允许列表列举的是你真正要发布的东西。
两个第三方代理被提交进了我的仓库
审计已检入的内容——不是将要发布什么,而是git里有什么——翻出了别人公开仓库的两份完整副本:一个GitHub分析代理(10个文件)和一个天气代理(7个文件),被塞进我的现场测试目录,连原始README都还在,告诉读者去上游克隆它们。
它们只被文档引用,没有任何东西依赖它们。我把它们取消追踪、加入gitignore,并在重新克隆表里做了记录,保证现场测试可复现。证据目录——真正的现场测试结果和Docker证据——原封不动,因为审计的意义正在于此:移走借来的代码,留下凭据。
让人安心的是:一次覆盖每个提交的全历史密钥扫描早已返回干净结果——零发现。这是许可和卫生问题,不是凭据泄漏问题。两项发现都进了安全审计。
我的CI从来没有绿过,一次都没有
最近每次推送都显示同样的红色❌。我一直在看本地测试套件(make test:绿色,925个通过),把CI当成噪音。
失败日志说出了真相:严格的mypy步骤在pytest运行之前就失败了——每次推送,持续数周。这意味着那些依赖Postgres的测试从未在CI里执行过。我的"CI不稳定",实际上是"CI一直在测试一个和我以为的不一样的、更小的系统,而且连那个都没测成功"。
阻塞本身的修复只有一行——一个类型注解。一行,背后藏了四个真实的测试失败,藏了数周。最糟的那个在下面。
防篡改审计日志,防不住篡改
当CI的pytest终于跑在Postgres上时,它在一个我巴不得它失败的测试上失败了:篡改审计日志,验证它能否检测到篡改。
测试直接改了数据库里的actor列,期望verify()返回False。它返回了True。原因是一个经典的反规范化陷阱:审计日志把每条记录存了两遍——一遍在正规列里,一遍作为JSON负载——而验证从负载重建记录,拿负载校验哈希链,被篡改的列却躺在那里没人读。
一个拿篡改者碰不到的副本来验证的防篡改日志,不叫防篡改。修复方案是从权威列读取被哈希的字段。这个bug在本地一直隐形,因为依赖Postgres的测试在没有数据库时会跳过——我绿色的本地套件,对一个它从未真正检验过的安全属性,一直是绿的。
提示:用篡改来测试防篡改。只做验证的测试证明链能自洽,但对"编辑能否被检测到"只字未提。
pytest不等于python -m pytest
最后一条很小,但它解释了一整类"在我机器上能跑":
- Makefile跑的是python -m pytest,它会把当前目录加进sys.path。本地天天绿。
- CI跑的是pytest控制台脚本,它不会。两个导入顶层examples包的测试模块在收集阶段就失败了——而且一直卡在mypy那堵墙后面,从头到尾都在收集阶段失败。
而在修复后续的警告过滤器时,我吃了个苦头才明白:pytest的filterwarnings条目按冒号切分——一条包含冒号的消息正则,让pytest试图去导入一个字面叫
我学到了什么
一道永远失败的闸门,会藏住一切。数周的红色CI训练我停止阅读它——而在这片噪音里,四个真实的测试失败无人注意。一道永远红的闸门,在操作上等同于没有闸门,只是更糟:它制造了忽视信号的习惯。
你的本地套件,对它从未运行过的东西是绿的。被跳过的Postgres测试不是失败,而是沉默——而在绿色的终端上,沉默读起来和成功一模一样。
在注册表把它变成永久之前,检查你的产物。发布不可逆。上传前那30秒的tar -tzf,是我跑过的最便宜的质量闸门。
审计检入了什么,而不只是发布什么。那两个第三方代理从未进过wheel——但仓库也是产品的一部分,而它刚刚公开。
五项发现都已修复,并随v0.1.0发布——这正是本文真正的论点:发布日在几小时内找到了数周开发悄悄漏掉的东西,因为它是第一天所有事情必须同时成立。
参考
- Secu
热门跟贴