Skip to content

perf(ci): 四阶段 DAG + warmup-tl 预热 + path filter + 字体缓存 + 时区/mirror 韧性#899

Merged
Liam0205 merged 19 commits into
masterfrom
feat/ci-dag-parallel
Jun 27, 2026
Merged

perf(ci): 四阶段 DAG + warmup-tl 预热 + path filter + 字体缓存 + 时区/mirror 韧性#899
Liam0205 merged 19 commits into
masterfrom
feat/ci-dag-parallel

Conversation

@Liam0205

@Liam0205 Liam0205 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

背景

PR #898 把 ctex 单包 4-engine 并行跑 (~20→8min), 但 CI workflow 整体仍受限于:

  • 5 个包串行在同一 job 内跑 (Test ctexxeCJK → ... → zhlineskip)
  • 所有 PR 都跑全 5 包, 即便只改了 CJKpunct
  • 字体每次 download (~1min/job)
  • TL install 间歇 ETIMEDOUT 让 windows job 反复挂

本 PR 把 CI 从单 job 串行重构成四阶段 DAG, 全链路加固.

架构

changes  →  warmup-tl  →  5 caller (test-{pkg})  →  test-result
  ↓             ↓                ↓
PR path     install TL    matrix.os × pkg          汇总
filter      到 cache      cache hit + verify
            (每 OS 1)    (2 mirror retry)

改动

1. 5 个 caller job + reusable workflow

test.yml 拆出 5 个 caller job (test-ctex / test-xeCJK / test-zhnumber / test-CJKpunct / test-zhlineskip), 各自 uses: ./.github/workflows/_test-package.yml. reusable workflow 内只剩 matrix.os 维度 (ubuntu/macos/windows).

为何不用 matrix.pkg 维度: GH Actions 在 strategy expansion 前注册 placeholder check 用未渲染 ${{ matrix.pkg }} on ... 作 name 然后 cancel, 留下"幽灵"job. 拆 5 caller job 用静态 name 彻底绕开.

2. changes job + PR path filter

dorny/paths-filter@v4 检测 PR 改动:

改动
ctex/** ctex
xeCJK/** xeCJK + ctex (依赖反查)
zhnumber/** zhnumber + ctex (依赖反查)
CJKpunct/** CJKpunct
zhlineskip/** zhlineskip
公共 (test.yml, _test-package.yml, scripts/check-parallel.sh, support/**, Makefile) 全 5 个

push / schedule / workflow_dispatch 触发不过滤, 全跑保险.

changes 同时输出 tl-version 透传给 caller (workflow_call input 不能直接引用顶层 env).

3. warmup-tl job (cache 预热)

每 OS 1 个 job 跑 setup-texlive. 收敛 install-tl 请求, 避免 5 caller × 3 OS = 15 路并发轰炸 mirror.

3 mirror retry: try 1 illinois (10min) → try 2 fau.de (10min) → try 3 mirror.ctan.org 自动重定向 (30min). 短超时让换 mirror 反应快.

4. caller mirror retry + verification

caller 端 setup-texlive 也 2 次 retry: try 1 mirror.ctan.org (auto-redirect, 15min), try 2 illinois 兜底 (15min).

新增 verification step (caller + warmup 各一个) — tlmgr update --self --list 2>&1 | grep "Unable to download\|not verified". tlmgr 在 mirror 不通时静默用本地老 db, setup-texlive 步骤退码 0 看不出, retry 不触发. verification step 把这种 silent fallback 检出, 强 fail 让 `rerun --failed` 可重跑.

5. `version: '2026'` 提取 `TL_VERSION` 顶层 env

setup-texlive 默认 `version: latest`, 它的 `resolveVersion → checkRemoteVersion` 早于 cache restore, mirror 不通时即便 cache 命中也让 install-tl 步骤 ETIMEDOUT. 显式 pin `version` 跳过这步.

通过 `TL_VERSION` 顶层 env → `changes.outputs.tl-version` → workflow_call input 透传, TL 年度发版只需改一处.

6. 字体缓存

`actions/cache@v6` 缓存 `$GITHUB_WORKSPACE/.font-cache/` (zip 解完即删, 只留 ttc, 200MB→100MB). 用 workspace 绝对路径避开 msys2 `~` expansion 在 Windows 上的 bug. cache key 含 `_test-package.yml` hash, URL 改自动失效.

7. `test-result` 汇总 job

`needs: [warmup-tl, test-ctex, test-xeCJK, test-zhnumber, test-CJKpunct, test-zhlineskip]`. 把 warmup 也算进 needs, 防 warmup 失败 → caller 全 skipped → result 误绿. branch protection 只盯这一个 check.

8. `paths-ignore` 收窄

旧 ignore 含 `.github/workflows/**`, 改 workflow 自身的 PR 不跑 CI. 收窄到 `agentic-.yml` 三个, 让核心 workflow (test.yml / release.yml / lint-test-files.yml) 改动立即触发 CI 自验.

9. `_test-package.yml` 新增 `event-name` input

reusable workflow 内 `github.event_name` 永远是 `workflow_call`, 拿不到顶层触发事件. 5 个 caller 透传 `event-name: ${{ github.event_name }}`, reusable workflow 内用 `inputs.event-name` 替代, schedule 触发的 `--show-saves` 行为正确生效.

10. `check-pr-ci.sh` 修两个假阳

  • 时区不匹配漏检 bot 评论: `git show -s --format=%cI` 输出本地 timezone, GitHub API 返回 UTC, jq 字符串比较错配. 改用 `TZ=UTC git show -s --format=%cd --date=iso-strict-local` 强制 UTC Z 后缀.
  • placeholder cancelled job 误报失败: GH Actions matrix 在 strategy expansion 前注册 placeholder check 用未渲染模板作 name 然后 cancel. hook 之前按 name dedupe 后仍把它当 fail. 加 `select(.name | contains("${{") | not)` 过滤掉.

11. 其它

  • `scripts/pycache/` 加 `.gitignore`
  • `llmdoc/reference/build-and-test.md` 同步四阶段架构 + retry + verification + FORCE_UPDATE_CACHE 失败历史

实测加速

OS Test ctex (master) Test ctex (PR) 加速
ubuntu ~11min ~5.5min 2.0×
macos ~12min ~5min 2.4×
windows ~13min ~8.5min 1.5×

Workflow 总 wall-clock: master ~17min → PR ~13min (~25% 缩短).

PR 改单包 (例 CJKpunct) 时只跑相关 caller × 3 OS, ~1.5min, ~10× 加速.

⚠️ Merge 后需要手动迁移 branch protection rules

旧 status check 名:

  • `on ubuntu-latest`, `on macos-latest`, `on windows-latest` (3 个)

新 status check 名:

  • `test-result` (1 个, 推荐只盯它)
  • 或 15 个 `test- / on ` (细粒度)

`release.yml` 的 gating 用 `actions/workflows/test.yml/runs` 看整体 workflow conclusion, 不受 matrix 改名影响.

失败重试历史 (踩坑记录)

  • ❌ `FORCE_UPDATE_CACHE=1` 让 warmup 保 uniqueKey, 想让 caller 跳 70s update — 不生效 (GH cache primaryKey 精确优先, caller 命中老的, 跳过 uniqueKey)
  • ❌ `update-all-packages: false` (PR/push 只走 cache) — 9/15 job baseline 漂移 (cache 旧)
  • ❌ 单 mirror 3 次 retry — 同 mirror 长时间不通时 3 次都炸
  • ❌ `version: latest` (默认) — setup-texlive remote check 早于 cache restore, ETIMEDOUT 时 cache 救不了
  • ❌ msys2 bash `~` expansion 不一致 — Windows 上 `mkdir -p ~/.cache` 后 `cd ~/.cache` 找不到

每个都通过 commit message + 注释 + llmdoc 记录, 防再踩.

Liam0205 added 5 commits June 27, 2026 00:18
3 个优化点 + 1 个细节, 都在 test.yml + .gitignore:

1. **5 包独立 matrix job**: 原结构是单 job 串行跑 Test ctex → xeCJK
   → zhnumber → CJKpunct → zhlineskip. 改为 matrix os × pkg = 15
   个独立 job 并行, fail-fast: false. wall-clock 取最慢一个 = ctex
   on windows ~8.5min, 比原来 single-job 三 OS max(ctex=8.5min +
   其他 4 包串行) 总 ~10-13min 显著快.

2. **PR 上按修改包过滤**: 新增 changes job, 用 dorny/paths-filter@v4
   检测改了哪些包. 依赖反查: ctex 依赖 xeCJK + zhnumber, 后者改了
   也跑 ctex. 公共改 (.github/workflows/test.yml, scripts/check-parallel.sh,
   support/**, Makefile) 让 5 个全跑. push / schedule / workflow_dispatch
   触发不过滤, 全跑回归保险.

3. **字体缓存**: actions/cache@v6 缓存 ~/.cache/ctex-kit-fonts/, key
   含本 yml hash, URL 改 (升级字体版本) 自动失效. 估计每 job 省 1min.

4. **新增 test-result 汇总 job**: branch protection 只盯它就够,
   不必列 15 个 matrix slot.

scripts/__pycache__/ 加 .gitignore (之前 commit 时漏 ignore).
actionlint 揭示: GH Actions 在 strategy expansion 前求值 job-level if,
那时 matrix context 不可用. 上一版用 (matrix.pkg == 'ctex' && ...)
直接让 workflow startup fail (0 jobs registered).

修法: changes job 新增 Compute active packages step, 把 PR 上 path
filter 命中的 pkg 拼成 JSON 数组 (push/schedule/dispatch 触发时直接
给全 5 个). test matrix 用 fromJSON(needs.changes.outputs.packages)
动态生成 pkg 维度.

附带 if: != '[]' guard: PR 上若没改任何相关包 (例如纯改 README),
整个 test matrix 跳过, test-result job 视为 skipped.
之前 paths-ignore 含 '.github/workflows/**', 设计是让 dependabot 改
非主测 workflow 时不跑测试. 但代价: 改 test.yml 自身的 PR 也被 ignore,
**workflow 改动无法在该 PR 上验证**.

改为只 ignore 'agentic-*.yml' (agentic-pr-review / agentic-patrol /
agentic-llmdoc-updater 三个), test.yml / release.yml /
release-ctan-upload.yml / lint-test-files.yml 等核心 workflow 改了
立刻触发 CI 验证.

(本 PR 之前 push 没启动主测就是被 .github/workflows/** ignore 拦了)
PR #899 上 5/5 windows job 都死在 "Install Noto fonts" step. log 显示
mkdir -p ~/.cache/ctex-kit-fonts 在 line 1 没报错, 但 cd ~/.cache/ctex-kit-fonts
在 line 3 报 "No such file or directory". 同样的 ~ expansion 两次结果
不一致 — msys2 bash 在 Windows GitHub Actions 上的诡异 bug.

修法: 改用 ${{ github.workspace }}/.font-cache 绝对路径, 不依赖 ~:
- actions/cache@v6 的 path
- Install Noto fonts step 内 FONT_CACHE=$(github.workspace)/.font-cache
- ubuntu / macos 上 workspace 是 Linux 路径, 也是绝对路径, 无副作用
setup-texlive-action 自带 cache (TL 包目录), 但 update-all-packages
默认 true 让每次都跑 tlmgr update (~1min/job × 15 job = 浪费).

改: update-all-packages 仅在 schedule / workflow_dispatch 触发时为
true. PR / push 走 cache baseline, 节省 ~1min/job (并发取 max 大概
1-2min wall-clock).

代价: PR / push 上 TL 不再同步 TLnet 最新版. schedule 每周五跑一次
update-all 把 baseline 推进, workflow_dispatch 维护者强制刷新.
@github-actions

Copy link
Copy Markdown
Contributor

PR Review: perf(ci): DAG 并行 + PR path filter + 字体缓存

审查结论: REQUEST_CHANGES — 整体架构思路优秀 (DAG 并行 + path filter + 字体缓存三管齐下),但有 1 个阻塞问题需解决。


Critical (1)

🔴 update-all-packages: false 导致 PR 事件下 9/15 job 失败

文件: .github/workflows/test.yml (新增行 update-all-packages: ${{ ... }})

当前 CI 结果: ctex / xeCJK / zhlineskip 在所有 3 个 OS 上均失败 (9/15 job),而 master 分支 CI (使用 update-all-packages: true) 全绿。

本 PR 将 update-all-packages 改为仅在 schedule / workflow_dispatch 时为 true,PR/push 事件使用 TL 缓存 baseline。问题在于:仓库的 .tlg 基线是在 update-all-packages: true 环境下保存的,当 PR 使用缓存中的旧版 TL 包时,包行为差异会导致日志输出不匹配。

zhlineskip on ubuntu-latest 的失败日志确认了这一点 — basic-leading.pdftex.diff 是典型的 TL 包版本漂移导致的基线不匹配。

建议方案 (二选一):

  1. 保持 update-all-packages: true — 放弃这项优化,保证 PR CI 结果与 master 一致。每 job ~1min 的代价在 DAG 并行下影响有限 (wall-clock 取 max)。
  2. 先合并, 立即跑一次 workflow_dispatch 建立新 cache baseline, 然后后续 PR 才能用缓存。但这会有一个"第一次合并后 push CI 也会失败"的窗口期,不推荐。

Important (1)

🟡 Branch protection 规则需同步更新

旧 workflow 的 status check 名称为 on ubuntu-latest / on macos-latest / on windows-latest (来自 name: on ${{ matrix.runs-on }})。合并后变为 ctex on ubuntu-latest 等 15 个 + test-result

PR 描述中提到 branch protection 只需盯 test-result 一个 check,这是正确的简化。但合并前需确认:

  • GitHub repo settings 的 branch protection rules 已准备好切换到 test-result
  • release.yml 中通过 actions/workflows/test.yml/runs?head_sha= 轮询测试状态的门控逻辑是否受影响

建议在 PR 描述中补充合并后的 branch protection 迁移步骤。


Suggestions (2)

💡 字体缓存中保留了 .zip 文件,增大 cache 体积

文件: .github/workflows/test.yml - Install Noto fonts step

当前逻辑在 .font-cache/ 中下载 zip → 解压 ttc → touch .done,但 zip 文件仍留在缓存目录中。Noto CJK OTC zip 各约 100MB+, 缓存中多存 ~200MB。建议解压后 rm -f "$FONT_CACHE"/*.zip 再 touch .done。

💡 空 matrix 时残留 ${{ matrix.pkg }} on ${{ matrix.os }} 幽灵 job

CI 结果中出现了一个名为 ${{ matrix.pkg }} on ${{ matrix.os }} 的 cancelled job,这是 GitHub Actions 在 matrix 为空时 if guard 生效前短暂创建的幽灵 job。功能上无害 (被 test-result 的 skipped 检查覆盖),但视觉上略显混乱。这是 GitHub Actions 的已知行为,非本 PR 问题。


亮点

  • DAG 并行架构设计合理: changestest matrix → test-result 三阶段清晰,fail-fast: false 确保看到所有 OS/pkg 的结果
  • PR path filter 的依赖反查 (xeCJK/zhnumber 改动触发 ctex) 正确反映了 ctex/build.luacheckdeps 依赖关系
  • YAML anchor &all / *alldorny/paths-filter 的 filters 参数中使用是有效的 (YAML parser 在 action 内部处理)
  • paths-ignore.github/workflows/** 收窄到 agentic-*.yml 是关键修复 — 原来的规则会导致 test.yml 自身的修改无法触发 CI 验证
  • llmdoc 同步更新 及时反映了新架构,保持文档一致性
  • .gitignore__pycache__/ 是合理的清理

🤖 Generated with Claude Code

Liam0205 added 2 commits June 27, 2026 10:16
bot review 揭示 PR 上 9/15 job 失败的根因: 仓库 .tlg baseline 是在
update-all-packages: true (TLnet 最新) 下 save 的, 我上一 commit 把
update-all 改为只 schedule 触发, PR/push 走 cache baseline (旧版 TL),
TL 包行为漂移 → .log vs .tlg 不匹配, 全包都炸.

修法: update-all-packages 改回 true. 节省 1min 不值 PR CI 不一致.

顺手处理 review 一个 nit:
- 字体 zip 解压后 rm -f, 减小 cache 体积 ~200MB.

bot 提到的 branch protection 迁移已写进 PR description.

幽灵 ${{ matrix.pkg }} on ${{ matrix.os }} job 是 GitHub Actions 已知
matrix 启动行为, 非本 PR 问题, 不修.
PR #899 review 提到 ${{ matrix.pkg }} on ${{ matrix.os }} 残留一个
cancelled "幽灵" job. 根因: GitHub Actions 在 strategy expansion **前**
注册 placeholder check, 用未渲染的模板字符串作 name, expansion 后再
cancel. 这是 GH Actions 已知行为, 但能通过架构绕开.

修法: 拆 matrix.pkg 维度, 改为 5 个独立 caller job (test-ctex /
test-xeCJK / ...) 各自 `uses: ./.github/workflows/_test-package.yml`.
- 每个 caller job 名字静态 ("test-ctex" 等), 无 placeholder.
- reusable workflow 内只剩 matrix.os 维度, 3 个 OS, 全静态 include.
- changes job 输出 5 个 bool, 每个 caller `if: needs.changes.outputs.<pkg>`.
- test-result needs 5 个 caller, 检查各自 result (success/skipped 都 OK).

bot 说"非本 PR 问题不修", 但用户判定"不能留任何形式的技术债", 修.

llmdoc/reference/build-and-test.md 同步新架构.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 ecbaf5b..4da3de5
审查截止 4da3de5ef583f32d2a928b8e2853a9cb988b88c4

上次审查的 3 个问题全部处理到位:update-all-packages 已回退为 true;字体 zip 解压后立即删除;拆 5 caller job + reusable workflow 消除了幽灵 cancelled job。最终架构 changes → 5 caller → test-result 三阶段清晰,代码质量良好。

🟢 小问题 (1)

  • 文件: .github/workflows/test.yml 代码链接
  • 问题: 顶层 env 块中的 NOTO_SANS_URLNOTO_SERIF_URL 已无任何 job 引用——字体安装逻辑已完全移入 _test-package.yml(该文件有自己的 env 声明)。这两行是死代码。
  • 建议: 删除 test.yml 中的 env 块(仅含这两个变量),避免误导维护者认为它们仍在生效。

亮点

  • 5 caller job 消除幽灵 check: 将 matrix.pkg 维度拆为 5 个静态 caller job,每个内部只剩 matrix.os 维度(3 OS),彻底绕开 GH Actions 动态 name placeholder 的已知行为
  • Reusable workflow 封装整洁: _test-package.ymlcase 分支区分 ctex(并行 wrapper)、zhlineskip(失败 dump log)和其他包(直接 l3build check),逻辑清晰
  • 字体缓存 key 设计合理: hashFiles('_test-package.yml') 让字体 URL 变更自动 invalidate cache,且 zip 解完立删只缓存 ttc
  • test-result 汇总 job: branch protection 只需盯一个 check,简化维护
  • llmdoc 同步更新: 文档及时反映新的三阶段架构

🤖 Generated with Claude Code

PR #899 review nit: 字体 install 逻辑已移入 _test-package.yml (该文件
有自己的 env 声明), test.yml 顶层 env 块仅含 NOTO_SANS_URL /
NOTO_SERIF_URL, 没人引用, 死代码. 删.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 4da3de5..273ae74
审查截止 273ae7401939ed0b8e32ee6cfc9164eaf1fab285

增量仅 1 个 commit:删除 test.yml 顶层已无人引用的 NOTO_SANS_URL / NOTO_SERIF_URL env 块。字体 URL 已移入 _test-package.yml 的独立 env: 块,删除正确,无副作用。

🟢 小问题 (1)

  • 文件: llmdoc/reference/build-and-test.md L213
  • 问题: 上一 commit 更新了详细 CI 结构描述(L246+),但摘要段 L213 仍写 "当前 CI 在同一 job 中依次进入 ctex/xeCJK/…… 运行测试",与新的 5 caller job 并行架构不一致。
  • 建议: 改为类似 "当前 CI 拆为 5 个独立 caller job (test-ctex / test-xeCJK / test-zhnumber / test-CJKpunct / test-zhlineskip),各自调用 _test-package.yml 在 3 个 OS 上并行测试"。

🤖 Generated with Claude Code

bot review nit: L246+ 的详细说明已更新, 但 L213 的摘要段还在写
"同一 job 中依次进入 ctex/xeCJK/..." 旧串行结构. 改成 "5 个独立
caller job, 各自 uses _test-package.yml 在 3 OS 上并行".
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 273ae74..ed593d9
审查截止 ed593d91066bd973a007e0698bf0f4f3efc43ae0

增量仅 1 个 commit ed593d9:修正 build-and-test.md L213 摘要段,将旧的"同一 job 中依次进入"描述更新为"5 个独立 caller job 并行",与 L246+ 详细说明及实际架构一致。改动正确,无副作用。

历次 review 提出的所有问题均已处理:

  • update-all-packages 回退为 true(commit f854c3d
  • ✅ 字体 zip 解压后 rm -f(commit f854c3d
  • ✅ 5 caller job + reusable workflow 消除幽灵 cancelled job(commit 4da3de5
  • ✅ 删除 test.yml 顶层死 env(commit 273ae74
  • ✅ 文档摘要段同步(commit ed593d9

代码良好,无新问题。CI 当前 pending(changes 已通过,15 个 test job 运行中),建议等 CI 全绿后合并。合并后记得将 branch protection status check 从旧的 3 个 on <os> 迁移到新的 test-result

🤖 Generated with Claude Code

setup-texlive-action 自身无 retry / mirror-list / timeout 选项 (其 v4
action.yml 仅 9 个 input, 全部网络问题需自己处理). PR #899 上多次
windows job 因 install-tl 下载 192.17.3.223:443 (ctan.math.illinois.edu)
ETIMEDOUT 而 FAILED_TO_DOWNLOAD, 不是我们代码 bug 但拖累 test-result.

修法: 包 3 次 retry — try 1/2 都 continue-on-error: true, 后一个 if
前一个 outcome == 'failure'; try 3 不带 continue-on-error, 真失败才让
job 挂. setup-texlive-action 内部自己有 cache, 重试时若网络已恢复就
直接走 cache hit 路径, 不会重复装 (cache 是 actions/cache 持久化的,
跨 step 共享).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 4da3de5..5abee18
审查截止 5abee1841476beaec9e4baf2e72c95f3220a2ed6

3 个新增 commit 都是合理的收尾/加固工作:死代码清理、文档同步、TeX Live 安装重试。整体架构 changes → 5 caller → _test-package.yml → test-result 三阶段清晰,代码质量良好。

🟠 重要建议 (1)

  • 文件: _test-package.yml L120
  • 问题: github.event_name 在 reusable workflow 内的值是 workflow_call(GitHub Actions 已知行为),不是调用方的原始触发事件。这意味着 L3BUILD_EXTRA_OPTIONS 中的 github.event_name == 'schedule' && '--show-saves' || '-H' 在 schedule 触发时也会走 -H 分支,--show-saves 永远不会生效。
  • 建议: 在 _test-package.ymlworkflow_call.inputs 中新增一个 event-name 输入,由 test.yml 的 5 个 caller job 通过 with: event-name: ${{ github.event_name }} 透传,reusable workflow 内用 inputs.event-name 替代 github.event_name 判断。这也是该问题的标准解法。

🟢 小问题 (2)

  • 文件: _test-package.yml L71-L97
  • 问题: 3 次 retry 的 with: 参数完全重复(package-file, packages, repository, update-all-packages)。功能无误,但如果后续调整参数需要改 3 处。
  • 建议: 可接受。GitHub Actions 目前没有 step 级别的参数复用机制,YAML anchor 在 uses: step 内不适用。保持现状即可。

  • 文件: _test-package.yml L71-L97
  • 问题: 3 次 retry 都指向同一个 mirror (ctan.math.illinois.edu)。如果该 mirror 长时间不可用,3 次都会超时(总计最多 90 分钟)。
  • 建议: 非阻塞。setup-texlive-action 内部可能有自己的 mirror 选择逻辑。且 ETIMEDOUT 多为短时间网络抖动,重试同一 mirror 通常就能成功。

增量改动总结

Commit 评价
273ae74 删 test.yml 顶层死 env ✅ 正确清理,NOTO_*_URL 已移入 _test-package.yml
ed593d9 llmdoc L213 摘要同步 ✅ 描述从旧串行结构更新为 5 caller 架构,保持文档一致
5abee18 TeX Live 3 次 retry ✅ 模式正确:try1/2 带 continue-on-error,try3 不带,真失败才挂 job

亮点

  • retry 模式干净: continue-on-error + 条件 if 级联,比 shell 内 for i in 1 2 3; do 更清晰,且每次 retry 都有独立的 step 状态可在 CI UI 中查看
  • _test-package.yml paths-filter 已包含自身: _all anchor 中加入了 _test-package.yml,改 reusable workflow 也会触发全包测试
  • 字体 cache key 更新为 _test-package.yml: hashFiles 指向正确的文件(URL 定义所在文件),而非旧的 test.yml

🤖 Generated with Claude Code

bot review 揭示 2 个问题:

1. [Critical] reusable workflow 内 github.event_name 永远是 'workflow_call',
   不是顶层触发事件. L131 的 schedule 判定永远走 -H, --show-saves 失效.

   修法: workflow_call 加 event-name input, 5 个 caller 透传
   github.event_name. reusable workflow 内用 inputs.event-name 判定.

2. [Insight] PR 上 5 caller × 3 OS = 15 job 同时启动, cache 没填好时
   全部并发 cache miss → 都跑 install-tl → 同 mirror 被 5 路并发轰炸
   → 偶发 ETIMEDOUT 让其中一个 fail.

   修法: 新 warmup-tl job 收敛到每 OS 1 个 setup-texlive, 各 caller
   needs warmup-tl 串行. cache 一旦填好, 15 caller 100% 走 cache hit
   (~10-20s/job), 完全消除 mirror ETIMEDOUT 风险.

   weekly schedule 的 update-all 也挪到 warmup-tl: schedule /
   workflow_dispatch 触发时 update-all = true 推 baseline; PR/push
   走 cache. caller 内 update-all 仍 true 保险 (走 cache hit, 廉价).

   warmup-tl 自己保留 3 次 retry (try1/2 continue-on-error, try3 真
   失败让 job 挂), 因为它**唯一会真装 install-tl** 的地方, 必须能应对
   首次冷启动的 ETIMEDOUT. caller 内的 retry 不再需要, 删掉.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 5abee18..e809b91
审查截止 e809b916f07d90a7eeb2b9a4081bfefe03ab4604

上次审查的 Critical(github.event_name 在 reusable workflow 内恒为 workflow_call)已修复:新增 event-name input 由 caller 透传。同时新增 warmup-tl 预热层从根本上消除 5×3 并发 install-tl 导致的 mirror ETIMEDOUT。

整体架构演进为四阶段 DAG:changes → warmup-tl → 5 caller → test-result,逻辑清晰。

🟢 小问题 (2)

  • 文件: test.yml warmup-tl
  • 问题: warmup-tlupdate-all-packages 在 PR/push 时为 false,而 _test-package.yml 内硬编码 true。这意味着 PR/push 触发时 warmup 填充未 update 的 cache,但 15 个 caller 各自会再跑 tlmgr update --all(~10-20s,廉价)。架构上可接受,但如果 warmup 也用 true,caller 可以完全跳过 update(纯 cache hit)。
  • 建议: 当前设计已在 PR 描述中说明合理性(schedule 推 baseline vs PR/push 走 cache),且 caller 的 update-all-packages: true 保证了 .tlg baseline 一致性。保持现状即可。

  • 文件: test.yml warmup-tl
  • 问题: warmup-tl 增加了 DAG 关键路径长度(CI 显示 ubuntu 16s、macOS 56s),所有 caller 必须等待 warmup 完成。
  • 建议: 这是预期的 trade-off:用 ~1min 的串行预热换取 15 个 caller 的 100% cache hit + 消除 ETIMEDOUT 风险。Wall-clock 总时仍优于旧方案。无需改动。

增量改动验证

Commit 评价
e809b91 warmup-tl + event-name 透传 ✅ 修复 Critical:inputs.event-name 替代 github.event_name--show-saves 在 schedule 触发时正确生效

架构亮点

  • warmup-tl 设计精确: 收敛 install-tl 到每 OS 1 个 job,3 次 retry 仅在此处保留(唯一冷启动入口),caller 简化为单步无 retry
  • event-name 透传模式: reusable workflow 的标准解法,注释清晰说明了 GH Actions 的已知行为
  • _test-package.yml 注释更新到位: 准确解释了 update-all-packages: true 为何在 caller 中是廉价操作
  • paths-ignore 收窄到 agentic-*.yml: 关键修复 — 确保 test.yml / _test-package.yml 自身改动能触发 CI 自验

合并前提醒

  1. Branch protection 迁移: 合并后需将 status check 从旧的 on ubuntu-latest 等 3 个改为 test-result 1 个(PR 描述中已提及)
  2. Windows warmup-tl 仍在 pending: 建议等此轮 CI 全绿后再合并

🤖 Generated with Claude Code

1. [Critical] PR #899 上 warmup-tl on windows 3 次 retry 同 mirror
   (ctan.math.illinois.edu) 都 ETIMEDOUT 192.17.3.223:443. bot review
   提的 🟢 #2 完全应验了 — 同 mirror 长时间不可达时 3 次都炸.

   修法: try 2 换 https://ftp.fau.de/ctan/systems/texlive/tlnet (德国),
   try 3 换 https://mirror.ctan.org/systems/texlive/tlnet (全球自动重
   定向). 三个 mirror 同时不可用的概率极低.

2. [Critical] 之前 test-result 只看 5 个 test-* result, warmup-tl 失败
   → 5 个 test-* SKIPPED → test-result 判 skipped 为 OK → 错误绿了.

   修法: needs 加 warmup-tl, 检查它 result. warmup 失败 → test-result
   失败 → branch protection 正确反映 CI 状态.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 4da3de5..751705d
审查截止 751705dcdbc2692833910946abcba00f4bef1594

5 个增量 commit 质量扎实,解决了上轮审查后发现的实际 CI 问题。架构从 changes → 5 caller → test-result 进化为 changes → warmup-tl → 5 caller → test-result 四阶段,设计合理。

增量改动总结

  1. event-name 透传修复 (e809b91) — 关键 bug fix。reusable workflow 内 github.event_name 永远是 workflow_call,导致 L3BUILD_EXTRA_OPTIONS 在 schedule 触发时错误走 -H 而非 --show-saves。新增 event-name input 由 caller 透传,_test-package.yml L118 正确使用 inputs.event-name

  2. warmup-tl 预热 job (e809b91) — 收敛 install-tl 到每 OS 1 个 job,从根源消除 5 caller 并发 cache miss → 同 mirror ETIMEDOUT 的问题。设计思路优于单纯 retry。

  3. 多 mirror retry (5abee18 + 751705d) — 三次 retry 换不同 mirror(illinois → fau.de → ctan.org auto-redirect),实测验证了单 mirror 长时间不可达时的应对能力。

  4. test-result 纳入 warmup-tl (751705d) — 修复了 warmup 失败 → 5 caller 全 skipped → test-result 误判绿的漏洞。test.yml L277 正确检查 warmup 结果。

  5. 死代码清理 + 文档同步 (273ae74 + ed593d9) — test.yml 顶层 NOTO_*_URL env 已移至 _test-package.yml,旧引用删除正确;llmdoc 摘要段同步新架构。

🟢 小问题 (2)

  1. warmup-tl_test-package.ymlupdate-all-packages 策略差异

    当前行为:warmup 填充 base TL 缓存,每个 caller 各自跑 tlmgr update --all(~10-20s)。如果 warmup 也 update-all-packages: true,caller 的 update 会更接近 no-op。不过 GH actions/cache 是 immutable(同 key 不可覆写),所以即便 warmup 做了 update,caller 仍会跑 update 检查。实际性能差异极小,不影响正确性。

  2. CI 历史 run 中残留旧 workflow 的 warmup-tl on ${{ matrix.os }} 失败记录

    当前 CI checks 列表中可见一条 warmup-tl on ${{ matrix.os }} fail — 这是被 cancel-in-progress 取消的旧 run 的幽灵记录(与本 PR 最初要解决的 matrix 幽灵 job 类似)。功能无害,会随新 run 完成后消失。

CI 实时状态

当前最新 push 的 CI(run 28277174319):

  • changes (4s)
  • warmup-tl 全 3 OS 通过 (16s / 29s / 34s — cache 命中,非常快)
  • ⏳ 15 个 test job 正在运行

warmup-tl 的快速通过验证了缓存预热机制工作正常。等待 test jobs 完成即可确认整体 CI 绿。

合并前提醒

PR 描述中已提到的 branch protection 迁移仍需在合并后执行:旧 check on ubuntu-latest / on macos-latest / on windows-latest → 新 check test-result


🤖 Generated with Claude Code

PR #899 实测 windows caller 即便 cache key 一致也 install-tl ETIMEDOUT.
根因: setup-texlive-action 默认 version=latest, action 入口先跑
resolveVersion → checkRemoteVersion(repo) 联网查"TLnet 现在是什么版本",
**这一步早于 cache restore**. 联网到 illinois mirror 失败 → 整个 action 挂.

修法: 4 处 setup-texlive-action 调用都加 version: '2026' 显式 pin, 跳
过 remote check. cache 命中后无任何联网, 真正实现"caller 100% 走 cache".

bot 提的"两个 🟢"里第二个(retry 同 mirror 风险)+ 这个 remote-check 联网
盲点合在一起就是 windows 链路问题. 现在彻底关上.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 751705d..443de49
审查截止 443de49e3f9d8cb20d579b3ae06b684688bae573

新增 1 个 commit:在全部 4 处 setup-texlive-action@v4 调用中显式 version: '2026',跳过 remote version check。修复精准——setup-texlive-action 的 resolveVersion → checkRemoteVersion 早于 cache restore 执行,mirror 不通时即使 cache 命中也会挂。这正是上轮 CI 中 windows caller cache key 一致但仍 ETIMEDOUT 的根因。

全量审阅 _test-package.yml(173 行新文件)和 test.yml(+242/-156)最终状态,架构四阶段 changes → warmup-tl → 5 caller → test-result 清晰完整,无阻塞问题。

🟢 小问题 (2)

  1. version: '2026' 硬编码需年度维护

    • 文件: _test-package.yml L80, test.yml L180/L195/L206
    • 问题: TL 2027 发布后需手动更新 4 处。commit message 已记录动机,但如果想减少未来维护负担,可考虑提取为 workflow 顶层 env.TL_VERSION 常量,4 处引用同一变量。
    • 影响: 低,年度操作且 CI 会立刻暴露版本不匹配。
  2. dorny/paths-filter@v4 版本锁定

    • 文件: test.yml L135
    • 问题: v4 是主版本 tag,跟随上游更新。若希望更严格的供应链安全,可考虑 pin 到具体 SHA(如 dorny/paths-filter@de90cc6...)。当前实践在开源项目中常见,非阻塞。

整体评价

PR 经过 13 个 commit 的迭代(含 2 轮 bot review 反馈),从初始的 matrix 并行方案演进到当前的 4 阶段架构,解决了:

  • 幽灵 cancelled job(拆 caller + reusable workflow)
  • mirror ETIMEDOUT(warmup-tl 预热 + 多 mirror retry)
  • reusable workflow event_name 永远是 workflow_call(input 透传)
  • msys2 ~ expansion bug(workspace 绝对路径)
  • setup-texlive-action remote check 盲点(pin version)

迭代过程体现了良好的工程实践——每个问题都是实测发现、定位根因、精准修复。

合并前提醒: Branch protection rules 需从旧 check(on ubuntu-latest 等 3 个)迁移到 test-result(1 个)。PR 描述已覆盖此项。


🤖 Generated with Claude Code

PR #899 实测各 caller 跑完后 cache hit, 但仍跑 ~70s 'Updating packages'
58 个包 — setup-texlive-action 设计是"primary key cache 命中时不 save",
update 后状态没回写, 每个 caller 自己重 update.

修法: warmup-tl 设 env SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE=1, 强制
setup-texlive 把 update 后的 TEXDIR 保到 **uniqueKey** (= primaryKey
+ random uuid). 因 GH actions/cache restore 用 restoreKeys 数组按
primaryKey prefix 模糊匹配, caller 不设此 env 时 cache restore 仍能
命中 warmup 的 uniqueKey, 拿到已 update 状态. caller 端
update-all-packages: false, 不再重跑 update.

warmup-tl 自身永远 update-all=true (无视触发器), 保证 cache 内容与
仓库 .tlg save 时的 TL baseline 一致.

实测预期: 每 caller 省 ~70s, wall-clock ~9min → ~7min.

源码确认 (TeX-Live/setup-texlive-action packages/action/src/cache.ts):
- SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE 是官方 env, 触发
  this.#forceUpdate = true
- forceUpdate 时 save 用 uniqueKey, 否则 primaryKey
- restore 用 restoreKeys = [primaryKey, oldPrimaryKey, ...], 按 prefix
  匹配, 能命中 uniqueKey
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 4da3de5..042442f
审查截止 042442fb30670daeef852d84aff0c9c286f1a215

7 个增量提交解决了上次审查后的实测问题,架构从"5 caller 直连"进化到 changes → warmup-tl → 5 caller → test-result 四阶段 pipeline。核心修复扎实,优化有效。

🟠 重要建议 (1)

  • 文件: .github/workflows/test.yml, .github/workflows/_test-package.yml
  • 问题: version: '2026' 硬编码在 4 处(warmup-tl 3 次 retry + _test-package.yml 1 处)。TeX Live 年度发版时需手动同步更新所有 4 处,遗漏任一都会导致 cache key 不匹配或版本不一致。
  • 建议: 考虑提取到 test.yml 顶层 env 变量(如 TL_VERSION: '2026'),warmup-tl 直接引用,_test-package.yml 通过新增 workflow_call input 透传。年度维护时只改一处。

🟢 小问题 (2)

  • 文件: llmdoc/reference/build-and-test.md
  • 问题: L219 摘要已更新为 5 caller 架构,但未提及 warmup-tl 预热阶段和 FORCE_UPDATE_CACHE 机制。这是本次改动的关键架构亮点(每 caller 省 ~70s),值得在文档中体现。

  • 文件: .github/workflows/_test-package.yml
  • 问题: caller 端 repository 仍指向 ctan.math.illinois.edu 单一 mirror。虽然 cache hit 场景下不会实际联网(version: '2026' 跳过 remote check + cache restore),但如果极端情况下 cache miss(如 GH cache eviction),caller 没有 retry/换 mirror 的能力。目前风险极低(warmup-tl 兜底),记录在此供参考。

亮点

  • event-name input 透传: 修复了 reusable workflow 内 github.event_name 永远是 workflow_call 的 GH Actions 已知问题,--show-saves 在 schedule 触发时能正确生效
  • 多 mirror retry 策略: illinois → fau.de → ctan.org 三级 fallback,从根本上解决单一 mirror 不可达的脆性问题
  • version: '2026' pin: 巧妙地跳过 setup-texlive-action 在 cache restore 之前的 remote version check,解决了 windows 上 cache key 匹配但仍 ETIMEDOUT 的诡异问题
  • FORCE_UPDATE_CACHE=1: 利用 setup-texlive-action 的官方 env,warmup 的 update 结果通过 uniqueKey 传递给 caller,避免 15 job 各自重跑 ~70s update
  • 迭代式问题修复: 7 个 commit 展现了"实测 → 定位根因 → 修复 → 再验证"的工程循环,每个 commit message 都详细记录了根因分析

🤖 Generated with Claude Code

PR #899 上一 commit 042442f 让 caller update-all=false 试图复用 warmup
update 后的 cache, 实测 3 个 caller 测试 fail (xeCJK macos / ubuntu,
zhlineskip ubuntu) 因 baseline 漂移.

根因: setup-texlive-action 用 actions/cache, GH cache 设计是 primaryKey
精确优先. warmup 用 SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE=1 保到
uniqueKey (= primaryKey + uuid). caller restoreKeys 列表中 primaryKey
精确命中老的 (无 uuid) entry — 那是 update 前的 TL baseline. caller
拿到旧 baseline + 不 update → 跟仓库 .tlg (在 update 后 save 的) 不一致.

修法: caller 回退 update-all=true. 每 caller 跑 70s tlmgr update 是
setup-texlive cache 设计的固有代价, 无合理方案绕开 (绕开需手动 install-tl
+ 自己管 actions/cache + 多 OS 适配, 400+ 行 yml). 总 wall-clock 仍优
旧 13min (现 ~9min).

如要进一步消除 70s, 见 follow-up issue (手动 install-tl 路径).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 042442f..5d19167
审查截止 5d19167cf5491d188663dd3f837c2df075e62ee6

增量仅 1 个 commit:回退 caller 端 update-all-packagesfalsetrue。决策合理——GH actions/cache精确 primaryKey 优先 匹配机制导致 FORCE_UPDATE_CACHE 的 uniqueKey 被跳过,caller 始终拿到 update 前的旧 cache entry,造成 .tlg baseline 漂移。注释详细记录了根因和权衡(70s/caller overhead vs 可靠性),是正确的工程判断。

🟢 小问题 (2)

  1. 文件: llmdoc/reference/build-and-test.md L219

    • 问题: 摘要段仍写"同一 job 中依次进入 ctex/xeCJK/…运行测试",与当前 5 caller + reusable workflow 架构不符。上次审查已提及但尚未更新。
    • 建议: 更新为"5 个独立 caller job(test-ctex / test-xeCJK / …)各自 uses: _test-package.yml,在 3 OS 上并行运行"。
  2. 文件: .github/workflows/_test-package.yml L63-L91

    • 问题: warmup-tlSETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE=1 注释仍声称"caller restoreKeys prefix 模糊匹配能命中 uniqueKey,拿到 update 后的 TL,跳过 update",但本 commit 的实测结论是这不生效(GH cache 精确优先)。warmup-tl 中的 FORCE_UPDATE_CACHE env 和相关注释与 caller 端回退 update-all=true 的实际行为矛盾——warmup 仍在写 uniqueKey,但 caller 不会命中它。
    • 建议: 考虑在 warmup-tl 的注释中补充说明 FORCE_UPDATE_CACHE 实测未达预期(GH cache 精确优先导致 caller 命中旧 primaryKey),保留该 env 仅作为"以防未来 GH cache 行为变更"的前瞻性设置;或者直接移除该 env 以避免误导。

整体评价

PR 经过 15 个 commit 的充分迭代,最终架构 changes → warmup-tl → 5 caller → test-result 四阶段 pipeline 已稳定。本次回退是对"过早优化"的务实修正——接受 70s/caller 的 tlmgr update 开销换取可靠的 baseline 一致性,总 wall-clock 仍从旧串行方案的 ~13min 降至 ~9min,收益显著。

合并前提醒(同前):Branch protection rules 需从旧 check(on ubuntu-latest 等 3 个)迁移到 test-result(1 个)。


🤖 Generated with Claude Code

实测 PR #899 上 hook 报 "✓ all CI checks passed, no new review activity"
但 bot 实际已在 push 后写了评论, 漏检.

根因: head_committed_at 用 git show -s --format=%cI 输出本地 timezone
(+08:00, 因为我在 CST 时区 commit). GitHub API created_at / submittedAt
返回 UTC (Z 后缀). jq 字符串比较按字母序:
  "2026-06-27T04:23:05Z"  # bot UTC 评论 (push 后)
  "2026-06-27T12:05:26+08:00"  # head commit 本地时间 (实际等价 UTC 04:05)
字符串排序 Z < +, 04: < 12:, 所以 bot 评论被判为 < head commit, 误判为
"push 前的旧评论".

修法: 用 TZ=UTC + git show --format=%cd --date=iso-strict-local. git
默认 %cI 是 commit timezone 不受 TZ env 影响, 必须用 --date=iso-strict-local
配合 TZ=UTC 才让 git 重新格式化为 UTC Z 后缀. 实测:
  本地 commit 时间 12:21:03 +08:00
  → TZ=UTC git show -s --format=%cd --date=iso-strict-local
  → 2026-06-27T04:21:03Z

现在 jq 比较两边都是 UTC Z 字符串, 字母序 = 时间序, 比较正确.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 4da3de5..c282286
审查截止 c282286c47abbf3efe479f106148650ffb919196

上次审查后新增 9 个 commit,涵盖:warmup-tl 预热 job 引入、event-name 透传修复、多 mirror retry、version pin、FORCE_UPDATE_CACHE 尝试与回退、死 env 清理、githook 时区修复。整体改动逻辑清晰,迭代过程中每个 bug 都有对应修复且注释充分,最终架构 changes → warmup-tl → 5 caller → test-result 四阶段稳固。

🟡 重要建议 (1)
  • 文件: test.yml warmup-tl 注释
  • 问题: warmup-tl 注释中写 "实测每 caller 省 ~70s",但 _test-package.yml 的注释明确说明 FORCE_UPDATE_CACHE 因 GH cache 精确优先匹配老 primaryKey 而实际不生效,caller 仍需 update-all-packages: true。两处注释矛盾,容易误导后续维护者。
  • 建议: 更新 warmup-tl 注释,删掉 "实测每 caller 省 ~70s" 的描述,改为说明 FORCE_UPDATE_CACHE 的实际行为(save 到 uniqueKey,但 caller 端 GH cache 精确优先命中旧 primaryKey,未能如预期跳过 update)。FORCE_UPDATE_CACHE env 本身保留无害(warmup 自身 save 行为改变不影响正确性),但注释需如实描述。
🟢 小问题 (2)
  • 文件: _test-package.yml L99
  • 问题: version: '2026' 硬编码年份。2027 年 TL 发布后需手动更新,否则 CI 会一直装 2026 版。
  • 建议: commit message 中已记录此 tradeoff,可在注释中补一句提醒(如 # TODO: TL 年度发布后更新此值),或接受现状由 schedule CI 自然暴露。

  • 文件: test.yml warmup-tl
  • 问题: warmup-tl 3 次 retry 的 timeout-minutes: 30 意味着极端情况下单 OS warmup 可能耗时 90 分钟。虽然 try 1/2 有 continue-on-error: true 在失败时快速进入下一个,但如果 mirror 超时而非拒绝连接,每次 try 可能要等满 30 分钟。
  • 建议: 考虑把 try 1/2 的 timeout 缩短到 10-15 分钟,仅 try 3 保留 30 分钟。冷启动正常完成一般在 3-5 分钟内。

亮点

  • event-name 透传 正确修复了 reusable workflow 内 github.event_name 永远为 workflow_call 的 GH Actions 已知行为
  • 多 mirror retry (illinois → fau.de → ctan.org) 有效应对单点 ETIMEDOUT
  • version: '2026' pin 精准解决了 setup-texlive remote version check 早于 cache restore 的根因
  • warmup-tl 预热架构 将 install-tl 收敛到每 OS 1 个 job,从根本上消除 5 路并发 cache miss 轰炸 mirror 的问题
  • test-result needs warmup-tl 防止了 warmup 失败 → caller 全 skipped → test-result 错误绿灯的边界情况
  • check-pr-ci.sh 时区修复 TZ=UTC --date=iso-strict-local 确保与 GitHub API UTC 时间戳的可比性
  • 每个 commit message 都详细记录了根因和修法,迭代过程可追溯

🤖 Generated with Claude Code

bot review 跨多轮累计建议 + 实测 CI 失败问题, 一次性处理:

1. [Critical, CI fail] windows test-xeCJK update 阶段 mirror 抖动 →
   tlmgr 用本地老 db → cache 内 TL revision 不与 .tlg baseline 一致 →
   environ01.xetex.diff fail. 修法: caller 加 2 次 retry 换 mirror
   (try1 illinois, try2 fau.de), 各 timeout 15min.

2. [Important] version: '2026' 硬编码 4 处 → 提取 TL_VERSION 顶层 env.
   workflow_call inputs 不能直接引用顶层 env, 由 changes job 输出
   tl-version 字段透传给 5 caller, 再透传到 _test-package.yml input.

3. [Important] warmup-tl 注释说 "实测每 caller 省 ~70s" 但实际不生效,
   与 _test-package.yml caller update-all=true 矛盾. 去掉 FORCE_UPDATE_CACHE
   env (实测无效, 留着误导) + 改写"历史"段如实记录 GH cache primaryKey
   精确优先导致 FORCE_UPDATE_CACHE 思路失败.

4. [Nit] warmup retry timeout 30/30/30 → 10/10/30, 让换 mirror 反应快.

5. [Doc] llmdoc 补 阶段 0.5 warmup-tl + caller retry 描述 + TL_VERSION
   设计 + FORCE_UPDATE_CACHE 历史. 三阶段说成四阶段.

不修 (bot 标记非阻塞):
- dorny/paths-filter@v4 没 pin SHA: 整仓 action 都没 pin, 改一处不协调,
  若要全仓 pin 是独立 PR.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 c282286..727b8f1
审查截止 727b8f1ca4fe31b84469734848df8deb15191bc9

上次审查后新增 1 个 commit,处理了前几轮 review 累积的 5 项建议。所有改动合理,前轮标记的重要建议(version: '2026' 硬编码、FORCE_UPDATE_CACHE 注释与实际行为矛盾、warmup retry timeout 过长)全部得到妥善处理。

🟢 小问题 (1)

  • 文件: _test-package.yml L88-L102
  • 问题: caller 端 retry 仅 2 次(try1 illinois, try2 fau.de),try2 没有 continue-on-error: true——如果两个 mirror 都在 Updating packages 阶段网络抖动,caller job 直接 fail,没有 mirror.ctan.org 全球重定向兜底(而 warmup-tl 有 3 次 retry 含全球重定向)。设计意图是 caller 已走 cache hit 路径、update 阶段联网比 install-tl 稳定,2 次足够。这个判断合理,但如果未来实测仍有 caller 因 update 阶段 mirror 抖动失败,可以考虑加第 3 次 mirror.ctan.org 兜底。

本次改动亮点

  • TL_VERSION 提取为顶层 env: 解决了前轮 review 反复标记的 version: '2026' 硬编码 4 处问题。通过 changes job output 透传(因为 workflow_call inputs 不能直接引用顶层 env),年度维护只改一处。设计干净。
  • FORCE_UPDATE_CACHE 清理: 移除了 warmup-tl 的 FORCE_UPDATE_CACHE env,注释改为如实记录"实测不生效"的历史,消除了与 caller 端 update-all=true 的矛盾。注释质量显著提升。
  • warmup retry timeout 优化: try 1/2 从 30min 缩到 10min,让换 mirror 反应更快;try 3 保留 30min 兜底。合理的渐进退避策略。
  • caller 端 retry: _test-package.yml 新增 2 次 retry 换 mirror,应对 cache hit 但 update 阶段 mirror 抖动的场景(PR perf(ci): 四阶段 DAG + warmup-tl 预热 + path filter + 字体缓存 + 时区/mirror 韧性 #899 windows 实测命中)。注释详细解释了为什么 caller 只需 2 次而非 3 次。
  • llmdoc 同步: build-and-test.md 完整覆盖了 warmup-tl 阶段、FORCE_UPDATE_CACHE 历史、TL_VERSION 设计、caller retry 机制,文档与代码保持一致。

合并前提醒

Branch protection rules 需从旧 check(on ubuntu-latest 等 3 个)迁移到 test-result(1 个)。PR 描述已覆盖此项。


🤖 Generated with Claude Code

PR #899 实测: hook 报 'CI FAILED warmup-tl on \${{ matrix.os }}' →
exit 1. 但该 check 是 GH Actions matrix 在 strategy expansion 前注册的
placeholder, conclusion 永远是 cancelled (expansion 完成后被 cancel),
不代表真失败.

之前的 dedupe 逻辑 (group_by(.name)) 救不了, 因为 placeholder name 含
未渲染的 \${{ matrix.X }} 字面, 与真实 matrix job (如 'warmup-tl on
ubuntu-latest') 是不同 name, 单独成组. 解法: 按 name 含 '\${{' 字面
过滤掉这类 placeholder.

注意当前 PR yml 已经修了 matrix.pkg 维度的幽灵 (拆 5 caller job), 但
warmup-tl 仍用 matrix.os 维度, 还是会产生
'warmup-tl on \${{ matrix.os }}' placeholder. 这是 GH Actions 固有
行为, 不可能彻底消除 placeholder; 只能在消费侧过滤.
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 ✅ APPROVE
审查范围 727b8f1..c501bfa
审查截止 c501bfa0ef9a6bae50afa35eef2f744d4637658a

增量仅 1 个 commit,修复 check-pr-ci.sh 两个实测命中的假阳性问题,代码质量良好,无阻塞问题。

✅ 增量改动分析

文件: .githooks/check-pr-ci.sh

修复 1 — matrix placeholder 假阳过滤:

GH Actions 在 strategy expansion 前注册 placeholder check,name 含未渲染的模板字面,随后 cancel。这些 placeholder 与真实 matrix job name 不同,group_by(.name) 去重救不了。

在 group_by 之前先 map(select) 排除含模板标记的 placeholder,逻辑正确且最小侵入。注释清晰说明了根因和为什么 dedupe 不够。

修复 2 — 时区不匹配漏检 bot 评论:

git show -s --format=%cI 输出 commit 自带 timezone(如 +08:00),而 GitHub API 的 created_at / submittedAt 是 UTC Z 后缀。jq 字符串比较按字母序,Z < +,导致 push 后的 bot 评论被误判为 push 前的旧评论。

改为 TZ=UTC git show -s --format=%cd --date=iso-strict-local 强制 git 输出 UTC Z 后缀,与 GitHub API 格式一致。注释详细记录了 %cI 为什么不受 TZ 环境变量影响的技术原因。

📋 整体 PR 状态总结

经过 18 个 commit 的迭代,本 PR 已将所有前序审查问题处理完毕:

前序问题 状态
update-all-packages: false 导致 baseline 漂移 ✅ 已回退为 true
字体 zip 增大 cache 体积 ✅ 解压后 rm -f *.zip
幽灵 cancelled job ✅ 拆 5 caller job 消除
event_name 在 reusable workflow 内恒为 workflow_call ✅ inputs.event-name 透传
mirror ETIMEDOUT 间歇失败 ✅ warmup-tl 3 mirror retry + caller 2 retry
TL version 硬编码 4 处 ✅ 提取 TL_VERSION 顶层 env
FORCE_UPDATE_CACHE 实测不生效 ✅ 已移除,注释如实记录历史
branch protection 迁移 ✅ PR 描述已说明
check-pr-ci.sh 假阳性 ✅ 本次 commit 修复

最终架构 changes → warmup-tl → 5 caller → test-result 四阶段清晰,代码注释充分,llmdoc 同步更新。

🟢 小问题 (1)

  • 文件: .github/workflows/test.yml L312
  • 问题: dorny/paths-filter@v4 未 pin commit SHA,存在 supply chain 风险
  • 建议: 与仓库其他 action 一致(均未 pin SHA),可作为独立 PR 统一处理,非本 PR 阻塞项

🤖 Generated with Claude Code

…allback

PR #899 实测: caller setup-texlive try 1 (illinois mirror) 报 success,
但实际 tlmgr 在 'Unable to download the checksum of the remote TeX Live
database' 时 silent 用本地老 db, setup-texlive 看 tlmgr 退码 0 认为
update 成功 → step success → retry 不触发. 结果 cache 内 TL 比仓库
.tlg baseline 老 → footnote01 / environ01 等测试 fail.

修法 (双管齐下):
1. caller try 1 换 mirror.ctan.org 全球自动重定向 (而非 pin illinois
   单点), GH runner 网络好时它会自动选最快 mirror. illinois 留 try 2
   兜底 (mirror.ctan.org 极端不可用时).
2. 加 verification step (caller + warmup-tl 各一个): setup-texlive
   完成后跑 tlmgr update --self --list, grep 输出"Unable to download|
   not verified" 字样, 命中就 exit 1 强 fail. 这样 tlmgr silent fallback
   时, 我们能在 setup-texlive step 后立即检测出来, step fail 后 rerun
   --failed 可以人工重跑 (网络抖动通常 transient).
@github-actions

Copy link
Copy Markdown
Contributor

🔍 PR 增量审查

项目 结果
结论 💬 COMMENT
审查范围 c501bfa0..5c12fc4a
审查截止 5c12fc4ac428cad0ac130e76c9935b3a9e5faa10

增量 1 个 commit 5c12fc4a:caller 和 warmup-tl 各新增 mirror verification step,防止 tlmgr 在 mirror 不通时 silent fallback 用本地老 db(setup-texlive 退码 0 看不出来)。同时 caller retry 的 mirror 顺序调整为 try 1 mirror.ctan.org(全球自动重定向)→ try 2 illinois(单点兜底)。

修复思路正确——tlmgr 的 silent fallback 是导致 PR #899 多轮 CI 失败的深层根因之一:setup-texlive step 成功但 TL revision 旧,后续测试 .log vs .tlg 不一致。verification step 用 tlmgr update --self --list 主动探测 mirror 可达性,grep 关键字 fail 掉,让 rerun --failed 可用。

🟠 重要建议 (1)

  • 文件: _test-package.yml L113-124, test.yml L230-240
  • 问题: verification step 在 caller 端仅有 2 次 retry(try 1 mirror.ctan.org, try 2 illinois),但 verification 是在 最后一个成功的 try 之后 独立跑的。如果 try 1 成功但 tlmgr update 阶段 silent fallback(mirror 间歇性不通),verification 会 fail,此时没有自动恢复路径——只能手动 rerun --failed。这对于 push 触发的 CI(非 PR)可能导致 false negative 需要人工介入。考虑是否值得在 verification fail 后自动重跑一次 setup-texlive(换 mirror),或者接受当前设计(手动 rerun 即可)。
  • 建议: 当前设计可接受(transient 网络问题用 rerun 解决),但建议在 verification step 的 ::error:: 消息中补充具体的 rerun 操作指引(已有,确认即可)。此条非阻塞。

🟢 小问题 (2)

  • 文件: _test-package.yml L113-124

  • 问题: verification step 用 tlmgr update --self --list 2>&1,如果 tlmgr 本身不在 PATH 中(理论上 setup-texlive 成功后不应发生,但 try 1 和 try 2 都 fail 时呢?),|| true 会吞掉错误,$out 为空,grep 不命中,step 显示 pass。不过在当前流程中 verification step 在 setup-texlive 之后、且没有 continue-on-error,如果两个 try 都 fail 了 try 2 没有 continue-on-error 会直接让 job fail,所以 verification 根本不会执行。逻辑正确,无实际风险。

  • 文件: test.yml L41-43

  • 问题: test.yml 顶层仍保留 NOTO_SANS_URL / NOTO_SERIF_URL 两个 env。上一轮 review 说的"删 test.yml 顶层死 env"对应 commit 273ae74,但当前分支 HEAD 上这两个 env 又出现了。可能是 rebase/merge 时意外恢复。这两个 env 在 test.yml 内无引用(字体逻辑已移入 _test-package.yml),属于死代码。

  • 建议: 删除 test.yml 顶层的 NOTO_SANS_URL / NOTO_SERIF_URL env 块。

整体评价

PR 经过 18 个 commit 的迭代,最终架构 changes → warmup-tl → 5 caller (_test-package.yml) → test-result 四阶段清晰。本轮新增的 verification step 补上了 tlmgr silent fallback 这个隐蔽的可靠性漏洞。注释详尽,每个修复都记录了根因和实测验证,对后续维护者非常友好。

待确认:

  1. test.yml 顶层 NOTO_SANS_URL / NOTO_SERIF_URL 是否需要清理(见上方小问题)
  2. CI 当前正在运行中,建议等本轮 CI 全绿后合并

🤖 Generated with Claude Code

@Liam0205 Liam0205 changed the title perf(ci): DAG 并行 + PR path filter + 字体缓存 perf(ci): 四阶段 DAG + warmup-tl 预热 + path filter + 字体缓存 + 时区/mirror 韧性 Jun 27, 2026
@Liam0205 Liam0205 merged commit a5c598a into master Jun 27, 2026
21 checks passed
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 上 5/5 windows job 都死在 "Install Noto fonts" step. log 显示
mkdir -p ~/.cache/ctex-kit-fonts 在 line 1 没报错, 但 cd ~/.cache/ctex-kit-fonts
在 line 3 报 "No such file or directory". 同样的 ~ expansion 两次结果
不一致 — msys2 bash 在 Windows GitHub Actions 上的诡异 bug.

修法: 改用 ${{ github.workspace }}/.font-cache 绝对路径, 不依赖 ~:
- actions/cache@v6 的 path
- Install Noto fonts step 内 FONT_CACHE=$(github.workspace)/.font-cache
- ubuntu / macos 上 workspace 是 Linux 路径, 也是绝对路径, 无副作用
Liam0205 added a commit that referenced this pull request Jun 27, 2026
bot review 揭示 PR 上 9/15 job 失败的根因: 仓库 .tlg baseline 是在
update-all-packages: true (TLnet 最新) 下 save 的, 我上一 commit 把
update-all 改为只 schedule 触发, PR/push 走 cache baseline (旧版 TL),
TL 包行为漂移 → .log vs .tlg 不匹配, 全包都炸.

修法: update-all-packages 改回 true. 节省 1min 不值 PR CI 不一致.

顺手处理 review 一个 nit:
- 字体 zip 解压后 rm -f, 减小 cache 体积 ~200MB.

bot 提到的 branch protection 迁移已写进 PR description.

幽灵 ${{ matrix.pkg }} on ${{ matrix.os }} job 是 GitHub Actions 已知
matrix 启动行为, 非本 PR 问题, 不修.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 review 提到 ${{ matrix.pkg }} on ${{ matrix.os }} 残留一个
cancelled "幽灵" job. 根因: GitHub Actions 在 strategy expansion **前**
注册 placeholder check, 用未渲染的模板字符串作 name, expansion 后再
cancel. 这是 GH Actions 已知行为, 但能通过架构绕开.

修法: 拆 matrix.pkg 维度, 改为 5 个独立 caller job (test-ctex /
test-xeCJK / ...) 各自 `uses: ./.github/workflows/_test-package.yml`.
- 每个 caller job 名字静态 ("test-ctex" 等), 无 placeholder.
- reusable workflow 内只剩 matrix.os 维度, 3 个 OS, 全静态 include.
- changes job 输出 5 个 bool, 每个 caller `if: needs.changes.outputs.<pkg>`.
- test-result needs 5 个 caller, 检查各自 result (success/skipped 都 OK).

bot 说"非本 PR 问题不修", 但用户判定"不能留任何形式的技术债", 修.

llmdoc/reference/build-and-test.md 同步新架构.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 review nit: 字体 install 逻辑已移入 _test-package.yml (该文件
有自己的 env 声明), test.yml 顶层 env 块仅含 NOTO_SANS_URL /
NOTO_SERIF_URL, 没人引用, 死代码. 删.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
setup-texlive-action 自身无 retry / mirror-list / timeout 选项 (其 v4
action.yml 仅 9 个 input, 全部网络问题需自己处理). PR #899 上多次
windows job 因 install-tl 下载 192.17.3.223:443 (ctan.math.illinois.edu)
ETIMEDOUT 而 FAILED_TO_DOWNLOAD, 不是我们代码 bug 但拖累 test-result.

修法: 包 3 次 retry — try 1/2 都 continue-on-error: true, 后一个 if
前一个 outcome == 'failure'; try 3 不带 continue-on-error, 真失败才让
job 挂. setup-texlive-action 内部自己有 cache, 重试时若网络已恢复就
直接走 cache hit 路径, 不会重复装 (cache 是 actions/cache 持久化的,
跨 step 共享).
Liam0205 added a commit that referenced this pull request Jun 27, 2026
1. [Critical] PR #899 上 warmup-tl on windows 3 次 retry 同 mirror
   (ctan.math.illinois.edu) 都 ETIMEDOUT 192.17.3.223:443. bot review
   提的 🟢 #2 完全应验了 — 同 mirror 长时间不可达时 3 次都炸.

   修法: try 2 换 https://ftp.fau.de/ctan/systems/texlive/tlnet (德国),
   try 3 换 https://mirror.ctan.org/systems/texlive/tlnet (全球自动重
   定向). 三个 mirror 同时不可用的概率极低.

2. [Critical] 之前 test-result 只看 5 个 test-* result, warmup-tl 失败
   → 5 个 test-* SKIPPED → test-result 判 skipped 为 OK → 错误绿了.

   修法: needs 加 warmup-tl, 检查它 result. warmup 失败 → test-result
   失败 → branch protection 正确反映 CI 状态.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 实测 windows caller 即便 cache key 一致也 install-tl ETIMEDOUT.
根因: setup-texlive-action 默认 version=latest, action 入口先跑
resolveVersion → checkRemoteVersion(repo) 联网查"TLnet 现在是什么版本",
**这一步早于 cache restore**. 联网到 illinois mirror 失败 → 整个 action 挂.

修法: 4 处 setup-texlive-action 调用都加 version: '2026' 显式 pin, 跳
过 remote check. cache 命中后无任何联网, 真正实现"caller 100% 走 cache".

bot 提的"两个 🟢"里第二个(retry 同 mirror 风险)+ 这个 remote-check 联网
盲点合在一起就是 windows 链路问题. 现在彻底关上.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 实测各 caller 跑完后 cache hit, 但仍跑 ~70s 'Updating packages'
58 个包 — setup-texlive-action 设计是"primary key cache 命中时不 save",
update 后状态没回写, 每个 caller 自己重 update.

修法: warmup-tl 设 env SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE=1, 强制
setup-texlive 把 update 后的 TEXDIR 保到 **uniqueKey** (= primaryKey
+ random uuid). 因 GH actions/cache restore 用 restoreKeys 数组按
primaryKey prefix 模糊匹配, caller 不设此 env 时 cache restore 仍能
命中 warmup 的 uniqueKey, 拿到已 update 状态. caller 端
update-all-packages: false, 不再重跑 update.

warmup-tl 自身永远 update-all=true (无视触发器), 保证 cache 内容与
仓库 .tlg save 时的 TL baseline 一致.

实测预期: 每 caller 省 ~70s, wall-clock ~9min → ~7min.

源码确认 (TeX-Live/setup-texlive-action packages/action/src/cache.ts):
- SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE 是官方 env, 触发
  this.#forceUpdate = true
- forceUpdate 时 save 用 uniqueKey, 否则 primaryKey
- restore 用 restoreKeys = [primaryKey, oldPrimaryKey, ...], 按 prefix
  匹配, 能命中 uniqueKey
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 上一 commit 042442f 让 caller update-all=false 试图复用 warmup
update 后的 cache, 实测 3 个 caller 测试 fail (xeCJK macos / ubuntu,
zhlineskip ubuntu) 因 baseline 漂移.

根因: setup-texlive-action 用 actions/cache, GH cache 设计是 primaryKey
精确优先. warmup 用 SETUP_TEXLIVE_ACTION_FORCE_UPDATE_CACHE=1 保到
uniqueKey (= primaryKey + uuid). caller restoreKeys 列表中 primaryKey
精确命中老的 (无 uuid) entry — 那是 update 前的 TL baseline. caller
拿到旧 baseline + 不 update → 跟仓库 .tlg (在 update 后 save 的) 不一致.

修法: caller 回退 update-all=true. 每 caller 跑 70s tlmgr update 是
setup-texlive cache 设计的固有代价, 无合理方案绕开 (绕开需手动 install-tl
+ 自己管 actions/cache + 多 OS 适配, 400+ 行 yml). 总 wall-clock 仍优
旧 13min (现 ~9min).

如要进一步消除 70s, 见 follow-up issue (手动 install-tl 路径).
Liam0205 added a commit that referenced this pull request Jun 27, 2026
实测 PR #899 上 hook 报 "✓ all CI checks passed, no new review activity"
但 bot 实际已在 push 后写了评论, 漏检.

根因: head_committed_at 用 git show -s --format=%cI 输出本地 timezone
(+08:00, 因为我在 CST 时区 commit). GitHub API created_at / submittedAt
返回 UTC (Z 后缀). jq 字符串比较按字母序:
  "2026-06-27T04:23:05Z"  # bot UTC 评论 (push 后)
  "2026-06-27T12:05:26+08:00"  # head commit 本地时间 (实际等价 UTC 04:05)
字符串排序 Z < +, 04: < 12:, 所以 bot 评论被判为 < head commit, 误判为
"push 前的旧评论".

修法: 用 TZ=UTC + git show --format=%cd --date=iso-strict-local. git
默认 %cI 是 commit timezone 不受 TZ env 影响, 必须用 --date=iso-strict-local
配合 TZ=UTC 才让 git 重新格式化为 UTC Z 后缀. 实测:
  本地 commit 时间 12:21:03 +08:00
  → TZ=UTC git show -s --format=%cd --date=iso-strict-local
  → 2026-06-27T04:21:03Z

现在 jq 比较两边都是 UTC Z 字符串, 字母序 = 时间序, 比较正确.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
bot review 跨多轮累计建议 + 实测 CI 失败问题, 一次性处理:

1. [Critical, CI fail] windows test-xeCJK update 阶段 mirror 抖动 →
   tlmgr 用本地老 db → cache 内 TL revision 不与 .tlg baseline 一致 →
   environ01.xetex.diff fail. 修法: caller 加 2 次 retry 换 mirror
   (try1 illinois, try2 fau.de), 各 timeout 15min.

2. [Important] version: '2026' 硬编码 4 处 → 提取 TL_VERSION 顶层 env.
   workflow_call inputs 不能直接引用顶层 env, 由 changes job 输出
   tl-version 字段透传给 5 caller, 再透传到 _test-package.yml input.

3. [Important] warmup-tl 注释说 "实测每 caller 省 ~70s" 但实际不生效,
   与 _test-package.yml caller update-all=true 矛盾. 去掉 FORCE_UPDATE_CACHE
   env (实测无效, 留着误导) + 改写"历史"段如实记录 GH cache primaryKey
   精确优先导致 FORCE_UPDATE_CACHE 思路失败.

4. [Nit] warmup retry timeout 30/30/30 → 10/10/30, 让换 mirror 反应快.

5. [Doc] llmdoc 补 阶段 0.5 warmup-tl + caller retry 描述 + TL_VERSION
   设计 + FORCE_UPDATE_CACHE 历史. 三阶段说成四阶段.

不修 (bot 标记非阻塞):
- dorny/paths-filter@v4 没 pin SHA: 整仓 action 都没 pin, 改一处不协调,
  若要全仓 pin 是独立 PR.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 实测: hook 报 'CI FAILED warmup-tl on \${{ matrix.os }}' →
exit 1. 但该 check 是 GH Actions matrix 在 strategy expansion 前注册的
placeholder, conclusion 永远是 cancelled (expansion 完成后被 cancel),
不代表真失败.

之前的 dedupe 逻辑 (group_by(.name)) 救不了, 因为 placeholder name 含
未渲染的 \${{ matrix.X }} 字面, 与真实 matrix job (如 'warmup-tl on
ubuntu-latest') 是不同 name, 单独成组. 解法: 按 name 含 '\${{' 字面
过滤掉这类 placeholder.

注意当前 PR yml 已经修了 matrix.pkg 维度的幽灵 (拆 5 caller job), 但
warmup-tl 仍用 matrix.os 维度, 还是会产生
'warmup-tl on \${{ matrix.os }}' placeholder. 这是 GH Actions 固有
行为, 不可能彻底消除 placeholder; 只能在消费侧过滤.
@Liam0205 Liam0205 deleted the feat/ci-dag-parallel branch June 27, 2026 05:55
Liam0205 added a commit that referenced this pull request Jun 27, 2026
上个 commit 给 Verify TL mirror reachable 的 if 加 tl1/tl2 success 守卫
(`(tl1.outcome == 'success' || tl2.outcome == 'success')`), 但 caller try 2
一直没声明 id, 导致 steps.tl2.outcome 始终空串. "tl1 失败 + tl2 成功" 路径
下 verify 不会跑, 丧失对 tlmgr silent fallback (Unable to download checksum →
退码 0 用本地老 db) 的检测能力, 这正是 PR #899 修的洞.

PR #901 第三轮 bot review 抓到的回归. warmup test.yml 的 try 2 一直有 id: tl2,
caller _test-package.yml 漏了, 这次补齐.
Liam0205 added a commit that referenced this pull request Jun 27, 2026
PR #899 实测 caller 端 setup-texlive 平均 70s+, mirror 抖动时单 step
可飙到 10+min (master test-ctex 17min 就是这样). 因为 setup-texlive
内部 actions/cache 设计 "同 primaryKey immutable", warmup 跑了 update
也不能回写, caller 永远要自己再跑一次 tlmgr update --all (~70s) +
看 mirror 脸色.

绕开方案: 我们自己用 actions/cache@v6 缓存整个 TEXDIR 到 weekly key.
- key: tl-bypass-<runner.os>-<TL_VERSION>-<year-week ISO>
- warmup-tl save: 跑完 setup-texlive (cache: false 关内部 cache) +
  verification, job 末尾 actions/cache@v6 自动 save TEXDIR.
- caller restore: actions/cache@v6 restore TEXDIR + echo bin 路径到
  $GITHUB_PATH. 完全跳过 setup-texlive-action, 不联网, 秒过.
- cache miss (weekly 边界 race / cache eviction): caller fallback 到
  setup-texlive-action (3 mirror retry + verification).

时序: weekly schedule 改周一 UTC 12:00 (从周五挪过来), 紧贴 ISO 周
边界 (周一 00:00 UTC 翻周). 周一一过 cache key 翻新, 12:00 schedule
跑刷新 TL cache, 周内 PR/push 享用 cache hit.

setup-texlive 源码确认 (TeX-Live/setup-texlive-action packages/action/
src/runs/main/index.ts): 它只调用 tlmgr.path.add() 把 TEXDIR/bin/<platform>
加入 PATH, 没其它 env 注册. 所以 caller 跳过 setup-texlive 后只需自己
echo "$TEXDIR/bin/<platform>" >> $GITHUB_PATH 即可.

平台 bin 目录:
- ubuntu-latest:    bin/x86_64-linux
- macos-latest:     bin/universal-darwin (Apple Silicon, universal binary)
- windows-latest:   bin/windows

预期加速: caller 端 70-600s/job → 几秒. 总 wall-clock 进一步缩短.

附带:
- .gitignore 加 actionlint (本地 lint 二进制, 别 commit)
Liam0205 added a commit that referenced this pull request Jun 27, 2026
上个 commit 给 Verify TL mirror reachable 的 if 加 tl1/tl2 success 守卫
(`(tl1.outcome == 'success' || tl2.outcome == 'success')`), 但 caller try 2
一直没声明 id, 导致 steps.tl2.outcome 始终空串. "tl1 失败 + tl2 成功" 路径
下 verify 不会跑, 丧失对 tlmgr silent fallback (Unable to download checksum →
退码 0 用本地老 db) 的检测能力, 这正是 PR #899 修的洞.

PR #901 第三轮 bot review 抓到的回归. warmup test.yml 的 try 2 一直有 id: tl2,
caller _test-package.yml 漏了, 这次补齐.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant