文档索引在 Windows 上正常,上 Linux 就 404:一次跨平台死链自查

举报
yd_248934043 发表于 2026/11/03 11:05:06 2026/11/03
【摘要】 同一份仓库、同一个链接,Windows 和 macOS 点得开,Linux 上却 404。本文拆开大小写敏感这个根因,给出两步定位法、可放进 CI 的十行检查脚本,以及一份写文档索引时的自查清单。文档 CI/CD 跨平台

一次真实的排查起点

我在一个开源多智能体项目里做文档走查时,发现了一个几乎不会在本地暴露的问题:文档索引文件 SUMMARY.md 里写着这样的链接——

- [技能交响乐:技能检索、编排与分发](symphony-技能编排与分发.md)

而仓库里实际的文件名是 Symphony-技能编排与分发.md,首字母是大写的 S。

在 Windows 上点开完全正常,在 macOS 默认的 APFS 上也正常。但同一份仓库放到 Linux(Docker 构建、CI runner、静态文档站点生成)上,这个链接就是一个 404。

原因不复杂:NTFS 和默认配置的 APFS 是大小写不敏感的文件系统,symphony.md 和 Symphony.md 被当成同一个文件;而 ext4、overlayfs、以及绝大多数 CI 的运行环境都是大小写敏感的。文件系统层面能打开,链接才是"看起来没坏"。

为什么这类问题特别难自查

它同时满足三个"躲开所有检查"的条件:

  1. 本地复现不了:开发主力机是 Windows 或 macOS,永远走不到失败分支;
  2. 构建不报错:文件都提交了、Dockerfile 也能构建、单测也能跑,CI 绿得理直气壮;
  3. 只有人点的时候才炸:出问题的是读者,不是作者。而且在文档站点里往往表现为"翻到某一页白屏",连堆栈都没有。

更麻烦的是它有对称性。同一个坑,中英双语目录往往各踩一次:docs/en/SUMMARY.md 指向 symphony.md,docs/zh/SUMMARY.md 指向 symphony-技能编排与分发.md,而两个实际文件都是大写 Symphony 开头。修一处不算修完。

把它查干净的方法

手工逐个点太慢,而且总会漏。这里的关键是:不要靠渲染,直接比对"索引里写的文件名"和"仓库里真实存在的文件名"。

两步就够了。第一步,拿到仓库真实的文件清单(这里用 GitCode 的 contents API 逐层遍历,换成 git 命令也一样):

# 只需要目录与文件名,不下载内容
curl -s "$API/repos/$OWNER/$REPO/contents/$DIR" | jq -r '.[].name'

第二步,把索引里的相对链接全部抽出来,逐个做精确匹配(不是忽略大小写的匹配):

# 抽出 markdown 里的本地 md 链接
grep -oP '\]\(\K[^)]+\.md' docs/zh/SUMMARY.md \
  | grep -v '/' \
  | while read -r f; do
      [ -e "docs/zh/$f" ] || echo "MISSING: docs/zh/$f"
    done

[ -e ] 在 Linux 上是精确匹配的,所以大小写不一致会直接落到 MISSING 里。如果想让脚本在 Windows 上也能跑出同样的结论,就不要用 [ -e ],改成先列出目录清单、再做字符串精确比较——这一步很容易被忽略,但恰恰是"用不敏感的系统去检查敏感的问题"这个陷阱本身。

顺带提一个验证技巧:对同一个路径只改大小写,各发一次请求,比对 HTTP 状态码。404 与 200 摆在一起,任何人都无法用"我这边是好的"来反驳:

docs/en/symphony.md                 -> 404
docs/en/Symphony.md                 -> 200
docs/zh/symphony-技能编排与分发.md   -> 404
docs/zh/Symphony-技能编排与分发.md   -> 200

让它不再复发

查出来只是止血,真正要解决的是"下次提交还会这样"。仓库里既然已经有 repo-integrity 这类校验提交树完整性的 CI,把文档链接检查挂进去是最省事的位置——十行以内的 shell 就够:

set -eu
fail=0
for sum in docs/en/SUMMARY.md docs/zh/SUMMARY.md; do
  dir="$(dirname "$sum")"
  grep -oP '\]\(\K[^)]+\.md' "$sum" | grep -v '/' | while read -r f; do
    if [ ! -e "$dir/$f" ]; then
      echo "::error file=$sum::链接目标不存在(注意大小写):$f"
      fail=1
    fi
  done
done
exit $fail

两个细节值得写进注释:

  • 只在 Linux runner 上跑。放到 Windows runner 上跑等于白跑,因为不敏感的文件系统会把错误吞掉,得到一个"通过"的假信号。
  • 只校验相对链接。外链会随网络和站点状态抖动,把它塞进 CI 会变成噪声,最后大家习惯性忽略红灯,检查就失效了。

可以带走的一份清单

写文档索引(SUMMARY.md、_sidebar.md、mkdocs.yml 的 nav、docusaurus 的 sidebars)时,下面这几条能挡掉绝大多数同类问题:

  1. 文件名统一用 PascalCase 或全小写,不要混;中文文件名尤其容易因为输入法或大写键产生首字母大小写漂移;
  2. 索引里指向的文件名,从文件系统复制,不要手打;
  3. 新增/重命名文档时,"改文件名"和"改索引"必须在同一个提交里;
  4. CI 至少加一条"索引链接可达性"检查,并且跑在区分大小写的系统上;
  5. 排查时不要用编辑器或浏览器的预览来判断,它们在不敏感系统上会给你假阴性。

最后一句经验:凡是"我本地是好的"这类反馈,先问一句对方的文件系统是不是区分大小写。这一个问题能省掉一大半来回。同样的逻辑也适用于 imports、package.json 的入口、Dockerfile 的 COPY 路径、以及 shell 脚本里的 source——它们全都吃大小写的亏,而且全都只在部署那天才发作。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。