# 构建探针

> 对「构建产物」本身做的断言——问的不是「代码写对没有」，而是「生成出来的东西对不对」。

来源：https://xiaoxusop.github.io/letterpress/wiki/build-probe/
语言：zh-CN
类型：知识库条目

## 被引用

- [构建可复现比你想的难，而且它值得](https://xiaoxusop.github.io/letterpress/reproducible-builds/)

## 正文

一类检查：不看源码，只看**构建出来的产物**。

它和单元测试的分工很清楚：

| | 问的问题 | 够不着的地方 |
|---|---|---|
| 单元测试 | 这个函数算得对吗 | 它有没有被接进管线 |
| 构建探针 | 产物里真的是这样吗 | 为什么 |

## 为什么需要它

因为有一整类 bug，**单元测试永远抓不到**：

- 配置写了但没接线（`@astrojs/mdx` 装了却没注册进 `integrations`）
- 文件写好了但没人引用（`fonts.css` 从头到尾没被 import）
- 处理器换了 API（老写法被接受但**不执行**，只发一条弃用警告）
- 元素渲染了两遍（所有断言都问「存在吗」，没有一条问「出现几次」）

它们的共同点是：**不报错、构建成功、测试全绿，只是没效果。**

## 这个项目里的几条

- **内容发布探针**：真的放一个 `.md` 与 `.mdx` 进去，构建，看能不能产出页面；
  再放入草稿与占用 `/about/` 的文章，确认前者不会泄漏、后者会明确阻断构建。
  **读配置不够**——配置可以是对的而接线是断的
- **链接渲染契约**：正文里的 `[[方括号]]` 是不是真的变成了链接
- **字体加载契约**：`@font-face` 有没有进产物、字体总量有没有失控
- **区块唯一性**：同一区块在一页里不得出现两次
- **子路径契约**：六类载体（属性 / 脚本内路径 / 绝对 URL / 纯文本产物 / 内容清单 / NDJSON 全量导出）的
  前缀是否都正确

## 一条纪律：新检查必须反向验证

**把 bug 放回去，确认它真的报错、退出码为 1。**

这不是形式主义。这个项目里出现过三次「检查本身是坏的」，
而且**失效方向是相反的**：

- **漏检**：一条检查因为 `readdir` 在别的作用域、抛异常被 catch 吞掉，
  **扫了 0 个文件却报告「通过」**
- **漏检**：一条检查只匹配 `href="/x"` 形式的相对绝对路径，
  产物里 `https://` 开头的 URL 全部**不进正则**——于是「前缀翻倍」
  这个致命问题在它的注视下安然通过
- **误报**：还是那条检查。它后来改成对整段 HTML 跑正则来找属性，
  于是**正文里的代码示例也被当成链接**——就是上一段里 `href="/x"`
  那个写法（它此刻正作为一个反例出现在这里）。修法是把正则锚定到
  标签上，只认真正的属性：`<[^>]*\s(?:href|src)="…"`

**误报和漏检一样有害**：一条会红的检查，人第一反应是关掉它。
对模板尤其致命——使用者写一篇提到 `href="/foo"` 的文章，构建就失败，
而失败原因和他的文章毫无关系。

**一条从没失败过的检查不值得信任；
一条会无缘无故变红的检查，同样不值得信任。**

## 覆盖面要跟着「载体」走

同一个地址可能出现在六类地方，而每类的写法不同：

1. `href` / `src` **属性**中的相对绝对路径
2. `<script>` 块里**拼出来的**路径（模板字符串拼的 `import()` 参数）
3. `https://` 开头的**绝对 URL**（`og:image`、RSS 发现、JSON-LD）
4. **纯文本产物**里的链接（`llms.txt`、`robots.txt`）
5. **JSON 内容清单**里的结构化 URL
6. **NDJSON 全量导出**里每条记录的站点地址与内容 URL

**每漏一类，就漏掉一整类 bug。** 这些覆盖不是预先画出来的清单，
而是每次遇到真实漏检后补进契约的。

相关条目：[reproducible-builds](https://xiaoxusop.github.io/letterpress/reproducible-builds/)、[letterpress](https://xiaoxusop.github.io/letterpress/wiki/letterpress/)。
