Skip to content

fix(ci): Lychee 扫站点文档 content/docs,并加上每周 cron (#3449) - #3477

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-3449-lychee-scope
Aug 6, 2026
Merged

fix(ci): Lychee 扫站点文档 content/docs,并加上每周 cron (#3449)#3477
yinlianghui merged 1 commit into
mainfrom
claude/issue-3449-lychee-scope

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #3449

前提复核(origin/main)

issue 的前提成立,三条都当场复核过:

断言 复核结果
args 只扫 docs/** + README 成立
触发器只有 workflow_dispatch 成立(push / pull_request 仍注释掉)
文件数 183 vs 15 成立 —— lychee --dump-inputs 实测:content/docs 183、docs/ 15、README 1

改了什么

1. 扫描范围:两棵树都扫(PM 裁决 ①)

args 增加 content/docs/**/*.mdcontent/docs/**/*.mdx,保留原有的 docs/**README.md。输入文件数 16 → 199

2. 每周 cron(PM 裁决 ②)

schedule: - cron: '17 4 * * 0'。避开整点(GitHub 的 schedule 队列在 :00 最挤、延迟最久),周日仓库最安静。workflow_dispatch 保留;push / pull_request 继续注释掉 —— #3213 裁决 B 依然有效,注释块和理由一并留在文件里(理由原本没写,这次补上了)。

3. lychee.toml:这一步是必须的,不是顺手改的

只改范围会得到一个从第一次运行起就全红的 workflow。实测(新 args + origin/main 的 lychee.toml):

🔍 640 Total  🔗 207 Unique  ✅ 167 OK  🚫 353 Errors
其中 "Cannot resolve root-relative link" 316 条

content/docs 里有 297 处站内绝对链接(/docs/...)。Lychee 在构造 URI 的阶段就把它们判成硬错误,这早于 exclude 过滤 —— 所以往 exclude 里加 ^/docs^/ 实测完全无效(错误照报、退出码照样是 2)。

处理方式:root_dir 先把站内绝对路由解析进一个磁盘上不存在的哨兵命名空间,再由 exclude 整段跳过。root_dir 必须是绝对路径,而仓库在本地和 runner 上的绝对路径不同,所以它只能是一个与仓库无关的常量 —— 哨兵是被这条约束逼出来的。

没有让 Lychee 去解析这些路由:fumadocs 路由没有扩展名(/docs/guide/data-sourcecontent/docs/guide/data-source.md),判它需要一份真正的 路由→文件 映射,而那份映射已经存在 —— scripts/check-doc-links.mjs。在 lychee.toml 里再抄一份,只是多一份会各自漂移的副本。

顺带删掉了 remap = ["^/docs/(.*)$ file://./docs/$1"]:它从未生效过(remap 作用在已解析的 URL 上,而 /docs/x 在解析阶段就失败,压根走不到 remap),而且替换目标既少扩展名、指的又是内部的 docs/ 而不是 content/docs/

4. Pin 测试

scripts/__tests__/check-links-workflow.test.ts:范围不是硬编码 content/docs,而是从 apps/site/source.config.tsdir 读出来,再要求 workflow 覆盖该目录下每一个 md/mdx。硬编码等于把站点布局又抄一份 —— 这个 bug 本来就是这么来的。content 再搬家一次,测试当天变红。同时钉住 root_dir/exclude 这一对(拆掉任何一半都会重新弄坏)、schedule 存在、以及 pull_request/push 不存在。

5. 文档

content/docs/guide/ci-cd-pipeline.md 原本把这件事写成"已知缺口,追踪在 #3449",还把已删除的 remap 写成现行配置;docs-links.yml 的注释说 Lychee "dispatch-only 且扫 docs/"。都已更新。

验证

本地下载了真的 lychee 0.24.2 跑的,不是纸面推演。

范围(--dump-inputs,精确计数)

total inputs: 199 | content/docs: 183 | docs/: 15 | README: 1

反向验证 —— 方向是先预测再跑的,四条都按预测变红

回退 预测 实际
只回退 args 范围 覆盖断言红 × scans every markdown file the site is built from
只回退 lychee.toml 两条配置断言红 × resolves site-absolute routes … × carries no remap …
删掉 schedule cron 断言红 × runs on a schedule as well as on demand
放开 pull_request 门禁断言红 × does not gate pull requests

全部恢复后 Tests 7 passed (7)

测试 / 门禁

pnpm exec vitest run scripts/          → Test Files 11 passed | Tests 151 passed (151)
actionlint check-links.yml docs-links.yml → clean
node scripts/check-doc-links.mjs       → Docs links are valid.
node scripts/check-control-bytes.mjs   → OK (3673 tracked text files)
pnpm exec eslint <新测试>               → exit 0
tsc --noEmit --strict <新测试>          → exit 0
YAML 解析:triggers = ["workflow_dispatch","schedule"],schedule = [{cron: "17 4 * * 0"}]

scripts/ 不在任何 tsconfig 的 include 里(root tsconfig 只含 packages/examples/apps),所以仓库的 type-check 本来就不覆盖它 —— 上面那条 tsc 是我单独跑的,如实说明。

⚠️ 首次定时运行预期是红的,而且是真红

修好范围后离线跑一遍,还剩 37 个错误,全部是仓库内相对文件链接失效,与外链无关:

这些是既有的内容缺陷,不是本 PR 引入的,按 Prime Directive #10 另开 issue、不夹带进这个 PR 修。顺带暴露出一个门禁洞:scripts/check-doc-links.mjs 只校验 /docs/... 开头的 href,相对链接一律直接 return true —— 所以这 16 个失效链接一直没人管。同样另开 issue。

我特意没有--fallback-extensions md,mdx:它能把错误从 33 降到 6,但降下去的那 13 个正是 .md 写成 .mdx 的真缺陷(fumadocs 路由无扩展名,.md 后缀在站点上就是 404),等于用宽容的消费端把生产端的错误盖住。宁可报得难看,也不要报得好看但不真。


Generated by Claude Code

…y cron (#3449)

check-links.yml scanned `docs/**` (15 internal ADR/audit files) and README, but
the site publishes `content/docs/**` (183 files, per apps/site/source.config.ts
`dir: '../../content/docs'`). No published page had ever been link-checked; the
workflow was green about a tree nobody reads.

- args now sweep both trees: content/docs/**/*.{md,mdx} plus the existing
  docs/**/*.{md,mdx} and README.md (the internal tree's external links are
  worth sweeping and cost nothing).
- schedule: weekly cron '17 4 * * 0'. #3213's PR withheld the cron on purpose —
  a schedule over the wrong tree only produces a false-green report on a timer.
  With the scope corrected, ruling B's periodic-sweep intent becomes real.
  push/pull_request stay commented out: #3213 ruling B stands.
- lychee.toml: the scope fix alone would have shipped 316 hard errors.
  content/docs carries ~297 site-absolute links (/docs/...), which Lychee fails
  while CONSTRUCTING the URI — before `exclude` is consulted, so an `^/docs`
  exclude pattern cannot suppress them (measured). `root_dir` now resolves them
  into a sentinel namespace that the exclude list skips wholesale. Judging those
  routes stays scripts/check-doc-links.mjs's job.
  The old `remap = ["^/docs/(.*)$ file://./docs/$1"]` is removed: it had never
  fired (remap operates on a parsed URL; `/docs/x` fails to parse first) and
  pointed at the internal docs/ tree without an extension besides.
- scripts/__tests__/check-links-workflow.test.ts derives the expected scope from
  apps/site/source.config.ts rather than hard-coding content/docs, and pins the
  root_dir/exclude pair, the schedule, and the absence of a PR trigger.
- ci-cd-pipeline.md documented the gap as open (#3449) and the removed remap as
  live; docs-links.yml's comment said Lychee was dispatch-only on docs/. Both
  updated.
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 6, 2026 9:13am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-links.yml(Lychee)扫的是 docs/**(15 个文件),不是站点文档 content/docs/**(183 个文件)

2 participants