让 spec 与代码保持同步¶
只有当 spec 仍然描述着它底下的代码时,它才有用。SpexCode 通过每个节点的 code: 指针把节点与它治理的文件绑在一起,spex spec lint 沿着这张图去找两者已经分道扬镳的地方。它不判断文字是否仍与代码的含义相符,那需要人来读,但它能捕捉到 spec 已经陈旧的结构性迹象,且代价足够低,可以在每次提交时运行。
spex spec lint 检查什么¶
第一项检查是断链:如果一个节点的 code: 或 related: 列表指向一个不再存在的文件,那是错误,会阻断提交。指向空处的 spec 比没有 spec 更糟。
第二项是覆盖度。SpexCode 枚举项目中受 git 跟踪的源文件,对没有被节点认领的文件发出警告。文件清单取自 git 索引,构建产物和依赖不会计入,因此把工具指向整个项目是安全的。覆盖度是警告而非错误:它是已经漂出 spec 视野的那部分代码的清单,随时间逐步消化。
第三项是漂移(drift):某个被治理的文件有比其 spec 最后一次修订更新的提交。这是代码已经移动而 spec 可能没跟上的信号。漂移默认是警告,不过一个已经落后好几个版本的文件,会阻断触及它的提交。
lint 还会给出建议性警告:一个节点的正文膨胀成对实现的复述,一个节点分叉出过多子节点,或一个文件被过多节点认领。这些是可读性信号而非同步信号,在同一趟检查中一并浮现。
漂移是如何计算的¶
漂移从 git 实时推导。没有存储的哈希,也没有「上一次已知良好状态」的快照:SpexCode 比较某个被治理文件的最后一次提交,与修订其 spec 的最后一次提交。git 已经记录了这两者,没有额外的东西需要维护。
一个文件在其 spec 之后发生变化,并不总意味着 spec 错了:一次重构可以搬动代码,而意图仍然完全成立。对于这种情况,你确认这次变化:spex spec ack <node> --reason "<理由>" 记录该节点的 spec 依然成立。当意图本身已经改变时,没有捷径;你重写 spec 去描述新的行为,新版本自己就会清除漂移。
这些检查在哪里运行¶
每一份克隆都可以安装一个 git 钩子,在每次提交前运行同样的 lint,这样断链或严重漂移的节点在落地之前就会浮现。这很有用,但它是本地的、可绕过的:钩子位于仓库之外,一份跳过安装步骤的新检出没有它,任何提交都能用一个环境变量跳过它。
谁也无法跳过的强制手段,是在 CI 中于每次推送和拉取请求时运行 spex spec lint:无论开发者的机器上发生了什么,构建都会因 lint 错误而失败。把钩子当作快速反馈,把 CI 当作真正的关卡。lint 从历史中推导版本时间线和漂移,所以 CI 需要完整的 git 历史,而不是浅克隆。