Skip to content

check-doc-links 挂在 ci.yml 的 docs job 上,对「纯 docs PR」永远不会触发(paths-ignore 含 content/**) #3448

Description

@yinlianghui

背景

#3213 的维护者裁决是「A 进 PR 门禁」,落地方式为把 node scripts/check-doc-links.mjs 加进 ci.yml 的 docs job(已由修复 #3213 / #3292 的 PR 实现)。本 issue 记录该形态残留的一个结构性缺口,供维护者决定是否收口。

问题

ci.yml 的 on: 在 push 和 pull_request 两处都带 paths-ignore:

paths-ignore:
  - '**/*.md'
  - 'content/**'
  - 'docs/**'
  - 'apps/site/**'
  - '.changeset/**'

站点文档全部位于 content/docs/**,被 content/** 命中。GitHub 的 paths-ignore 语义是「改动文件全部命中忽略模式则整个 workflow 不启动」,且 GitHub 没有 per-job 的 path filter。

因此:一个只改 content/docs/** 的 PR 根本不会启动 ci.yml,docs job 不运行,新加的 Check docs links 步骤自然也不运行 —— 而「纯 docs PR」恰恰是最可能改坏文档链接的那一类 PR。

当前实际覆盖面:

PR / push 类型 链接检查是否运行
纯 docs PR(只改 content/**) ❌ 不运行(workflow 不启动)
docs + 代码混合 PR ✅ 运行
push 到 main(含非忽略路径) ✅ 运行

即坏链可以经由纯 docs PR 合进 main,直到下一个不相关作者推代码时才把 main 弄红 —— 归因错人。

仓库内已有的正确答案

control-bytes.yml 撞过同一堵墙,并在文件头把理由写清楚了:

Why this is its own workflow instead of a job in ci.yml or lint.yml: both of those list '**/*.md', content/**, docs/** and .changeset/** under paths-ignore, and GitHub has no per-job path filter. (…) a gate that cannot see a markdown-only PR rebuilds the hole it exists to close.

Hence: no paths and no paths-ignore here, deliberately.
scripts/__tests__/check-control-bytes.test.ts fails if either is ever added.

changeset-guard.yml 是同一模式的第二个实例(它是唯一以 .changeset/** 为触发路径的 workflow,AGENTS.md 已记载)。

check-doc-links.mjs 与 control-bytes 属于完全相同的类别:守 markdown/content、零 install、零网络、几秒钟跑完。

建议方案(供裁决)

方案 A(推荐)—— 独立 workflow,仿 control-bytes.yml
新建 .github/workflows/docs-links.yml,on: pull_request/push,触发路径为 content/** + scripts/check-doc-links.mjs;步骤只有 checkout + 一行 node scripts/check-doc-links.mjs。

  • 收益:纯 docs PR 被真正拦住,约 15 秒,无 install 无网络。
  • 成本:多一个 workflow 文件;与仓库既有先例一致。

方案 B —— 把 content/** / '**/*.md' 从 ci.yml 的 paths-ignore 移除

  • 代价很大:任何改一个错别字的 docs PR 都会拉起 type-check + 4 个 test shard + e2e + docs build(约 30 分钟 runner)。与仓库刻意的省 CI 决策冲突。不推荐。

方案 C —— 维持现状,接受纯 docs PR 不被门禁覆盖,靠 main 上的后续 push 兜底。

相关

Activity

  1. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    分诊 + 裁定:bug + pm:queue(objectui 分片 PM,会话 session_01GTRjn8xBqp75dk7kFupVRt)。

    PM 裁定:采方案 A——check-doc-links 提升为独立 workflow(无路径过滤),veto 窗口开放。

    这形式上偏离维护者 08-03 对 #3213 的「加进 ci.yml 的 docs 任务」原话,但那个裁决作出时结构洞尚未被发现;其意图(内链断裂 = 硬门禁)在字面落法下对纯 docs PR 恰好失效——而纯 docs PR 正是断链最常见的引入路径。仓内已有两个同类先例(control-bytes.yml、changeset-guard.yml)均为「无路径过滤的独立 workflow」,control-bytes.yml 头注自己写着:看不见 markdown-only PR 的门禁「rebuilds the hole it exists to close」。两轴:独立 workflow 是意图的忠实实现且零网络毫秒级(成本可忽略);保留 ci.yml 步骤与否由实现者按最小重复原则定。如维护者坚持字面落法,请在本单否决。

    派发前置:等 PR #3450 落地(避免同文件面);文件面 .github/workflows/(新 workflow)± ci.yml(若移除重复步骤)。


    Generated by Claude Code

  2. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    追加相邻观察(来自 #3451/PR #3456 的 dev,因 GitHub 搜索限流未能完成查重、按 #4949 纪律未盲目立单,由 PM 代记于本单——同属「CI 覆盖面」家族):

    scripts/ 不在根 tsconfig.json 的 include(仅 packages/examples/apps),也不是 workspace 包,故 turbo run type-check 从不类型检查 scripts/__tests__/*.ts——包括 ci-cd-pipeline-doc、vitest-invocation-guard、shadcn-local-patches 等一批门禁 pin 测试自身。它们靠 vitest 运行时执行,但无类型门。实现本单(check-doc-links 独立 workflow)时可一并评估:给 scripts/ 补 type-check 覆盖是否顺路;若不顺路,拆单另记。


    Generated by Claude Code

  3. self-assigned this
    on Aug 6, 2026
  4. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    认领:objectui 分片 PM 并行批第 43 单(前置解除:PR #3450 已 MERGED@223a92f26)
    会话:session_01GTRjn8xBqp75dk7kFupVRt
    分支:claude/issue-3448-doclinks-own-workflow
    Worktree:objectui-issue-3448
    文件面:.github/workflows/(新 workflow,无路径过滤,镜像 control-bytes.yml 形状)± ci.yml(若移除重复步骤)。顺路评估上方备注的 scripts/ type-check 覆盖,不顺路则报告拆单。裁定见上方分诊评论(采 A,veto 窗口开放)。
    与在飞第 41 单(#3426,plugin-list/grid + 语言包)文件面互不相交。


    Generated by Claude Code

  5. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    验收:ACCEPT → PR #3458(并行批第 43 单,会话 session_01GTRjn8xBqp75dk7kFupVRt),16/16 检查全绿,新 workflow 在本 PR 实测 9 秒、零安装跑完。ready + auto-merge 因 GraphQL 配额排队,恢复即执行。

    复核要点:

    1. A 裁定的落法超出草图:连 issue 自己建议的 paths: content/** 都被正确拒绝——那只是把脚本扫描面抄成第二份易漂配置,换不来可测量的节省;无过滤 + 触发配置的 YAML 解析证据 + 「不许出现任何 filter 键」的 pin 测试三层锁死。诚实声明覆盖极限:docs-only PR 场景只能由未来的纯 md PR 实证,今天靠配置语义与 pin 保证。
    2. ci.yml 步骤移除的判断正确:新 workflow 触发面是该步骤的严格超集,保留只会多一格红与一处遗忘点;原 KNOWN GAP 注释同步退役并留指路注释,pin 强制「一门禁一居所」。
    3. fence 偏差系门禁强制:ci-cd-pipeline.md 的既有测试要求每个 workflow 同 PR 补文档节(红证据已录)——不改就是明知红的 PR,四处编辑均必要。⚠️ 已知与在飞 PR docs(ci): 删掉 ci.yml 任务表里的幽灵 dev-server 行,并给任务表加双向 pin (#3451) #3456 在同页相邻行可能 rebase 冲突,两侧 hunk 均有意,合并队列若报冲突按两者叠加解。
    4. scripts/ type-check 判「拆单」的三点分析(安装前提冲突、tsconfig 架构决策、与在飞 docs(ci): 删掉 ci.yml 任务表里的幽灵 dev-server 行,并给任务表加双向 pin (#3451) #3456 文件持有冲突)成立——PM 稍后限流解除时代为立单,观察已记录在本单不丢。

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions