From 86746153f9d779f9762381ec2d23a0b8c3d022d5 Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 22:08:30 +0800 Subject: [PATCH 1/3] =?UTF-8?q?ci(m266):=20ci.yml=20=E5=8A=A0=E5=A4=9C?= =?UTF-8?q?=E9=97=B4=E5=AE=9A=E6=97=B6=E8=A7=A6=E5=8F=91=EF=BC=88schedule?= =?UTF-8?q?=2017=203=20*=20*=20*=EF=BC=89=EF=BC=8C=E8=A1=A5=20T30=20?= =?UTF-8?q?=E7=9A=84=E5=85=A8=E9=87=8F=E7=9F=A9=E9=98=B5=E7=BC=BA=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 只新增 schedule,push main / pull_request / workflow_dispatch 三条一字不改 - 文件头写清为什么是 schedule 而不是 push dev(成本/排队 vs T+1 发现延迟),这是故意的取舍 - concurrency.group 带上 github.event_name:定时与 push main 落在同一个 ref 上, 否则 cancel-in-progress 会让两者互相取消,夜跑被取消 = 那一夜没有结果 - 逐个 job 核过 github.event 依赖:只有 runtime-smoke-macos 的 workflow_dispatch 条件, schedule 下按设计跳过;其余 job 无 event 表达式,夜间不空转 --- .github/workflows/ci.yml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9f8391..d6d0425 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,18 +10,50 @@ name: CI # 见 DevDocs/00-踩坑记录-p2.md T7)。历史实现只作源码参考,**没有构建方式**(见 README: # 它的 manifest 整份是 workspace 继承写法,离开 workspace 后连解析都过不了)。 +# ---- 夜间全量矩阵(M266 / T30):为什么是 schedule,而不是 push dev ---- +# 上面那套门禁的前提是「有人往 main 推东西」,而本仓的实际节奏是**日常只推 dev**、main 一个月只在 +# 发版那一刻动一次 ⇒ 全量矩阵一个月跑一次,一次爆 5 个红,全部是早已存在的平台依赖问题。 +# `schedule` 就是补这个缺口的入口:每晚在 main 的末梢上跑一轮,把「平台回归的发现延迟」从一个月 +# 压到一天。 +# +# **不要顺手把它改成 `push: branches: [dev]`。** 本文件的矩阵 = 两个 OS 的 build release + 两个 OS 的 +# test + 两个 OS 的 clippy + fmt + runtime-smoke + update-e2e。挂到 dev 上等于每次提交烧一轮四平台 +# 矩阵,成本与排队都不可接受(dev 白天是开发时间);而 24 小时一轮就足以把平台回归压到 T+1。 +# 这是**故意的取舍**:抓得住「平台回归 T+1 就能看见」,抓不住「当天的错当天见」。dev 线的快守门 +# (fmt / clippy / guards / gpu 自测)在 dev-guards.yml,那边刻意不上全量 test / build,理由见其头部。 +# +# schedule 的三条语义边界(省得下次重新查): +# ① 定时只在**默认分支**上生效:cron 跑的是 main 的末梢,dev 上尚未合入的提交不进这一轮 +# (这是设计,不是遗漏:dev 的守门在 dev-guards.yml)。且这份文件必须先合进 main,cron 才会 +# 第一次响——合并之前一次都不会跑。 +# ② 失败可见性与 push 一样:`::error::` 注解由 test 作业的 shell 步骤自己产出,不读 event 上下文; +# 定时任务的失败同样会通知默认分支的维护者。 +# ③ 逐个 job 核过 `github.event` 依赖:整个文件只有 runtime-smoke-macos 的 +# `if: github.event_name == 'workflow_dispatch'` 看 event,在 schedule 下为假 ⇒ 该 job 跳过 +# (它本来就只是手动探针,见其注释);其余 job 与矩阵里没有任何 event 表达式,夜间不会空转。 +# +# `cron` 一律 UTC:`17 3 * * *` = 每天 03:17 UTC(UTC+8 的 11:17)。分钟刻意避开 :00 / :30 +# ——整点是定时队列最挤的时候。要挪到本地凌晨就把它改成 `17 19 * * *`,其余不用动。 + on: push: branches: [main] pull_request: workflow_dispatch: + # 夜间全量矩阵(M266 / T30,理由与取舍见文件头)。cron 是 UTC;17 分避开整点队列。 + schedule: + - cron: '17 3 * * *' env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 concurrency: - group: ci-${{ github.ref }} + # 加了 schedule 之后这一行必须带上 event_name:定时运行落在默认分支上,`github.ref` 同样是 + # `refs/heads/main`,与「push main」落进同一个组 ⇒ 配上 cancel-in-progress 就是两者互相掐死 + # (夜里那轮被白天的推送取消 = 那一夜没有结果,正是本次要填的洞)。带事件名之后: + # push↔push 仍按 ref 互斥(原有行为一字未变),定时自成一列,既不会被推送取消,也不去取消推送。 + group: ci-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true jobs: From 55f0e9bbf101d1c4d2358522ff6d83427d89981e Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 22:10:44 +0800 Subject: [PATCH 2/3] =?UTF-8?q?ci(m266):=20=E6=8C=89=E5=A1=94=E5=88=A4?= =?UTF-8?q?=E5=86=B3=E6=8A=8A=20cron=20=E6=8C=AA=E5=88=B0=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E5=87=8C=E6=99=A8=2003:17=EF=BC=88cron=20=E6=98=AF=20?= =?UTF-8?q?UTC=EF=BC=8C=E5=86=99=2017=2019=20*=20*=20*=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E9=BB=98=E8=AE=A4=E5=88=86=E6=94=AF=E8=BE=B9=E7=95=8C?= =?UTF-8?q?=E4=B8=8E=E8=AF=81=E6=8D=AE=E5=BC=BA=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cron 从 '17 3 * * *' 改成 '17 19 * * *'(17:19 UTC = 本地 UTC+8 的 03:17), 文件头写死「本地每天 03:17」,不留让读者自己换算的口子 - 文件头第 ① 条写严厉:schedule 只对默认分支生效、跑的是 main 末梢,dev 未合入的内容不在这一轮; 「dev 已经有人天天看着了」是错的(全量矩阵在 dev 上一次都没跑过) - 新增第 ④ 条:定时必须与 push main 分列 concurrency 组,因为取消不是红, 被静默取消的夜间入口会让「昨晚没报错」变成一句假话 - concurrency 注释里把结论降级为「GitHub 文档语义,未在真实 runner 上构造并发验证」 - docs/backend-cjk-evidence-matrix.md:三处 文件:行号 锚点改成作业名 + 步骤名(行号必再腐), 并加一条「锚点不用行号」的说明防复发 --- .github/workflows/ci.yml | 25 ++++++++++++++++--------- docs/backend-cjk-evidence-matrix.md | 10 +++++++--- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6d0425..83ec0a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,27 +22,32 @@ name: CI # 这是**故意的取舍**:抓得住「平台回归 T+1 就能看见」,抓不住「当天的错当天见」。dev 线的快守门 # (fmt / clippy / guards / gpu 自测)在 dev-guards.yml,那边刻意不上全量 test / build,理由见其头部。 # -# schedule 的三条语义边界(省得下次重新查): -# ① 定时只在**默认分支**上生效:cron 跑的是 main 的末梢,dev 上尚未合入的提交不进这一轮 -# (这是设计,不是遗漏:dev 的守门在 dev-guards.yml)。且这份文件必须先合进 main,cron 才会 -# 第一次响——合并之前一次都不会跑。 +# schedule 的四条语义边界(省得下次重新查): +# ① 定时只在**默认分支**上生效:cron 跑的是 **main 的末梢**,dev 上尚未合入的内容**不在这一轮里**。 +# 所以「dev 已经有人天天看着了」这句话是**错的**——dev 线只有 dev-guards.yml 那几道快守门 +# (fmt / clippy / guards / gpu 自测),全量矩阵在 dev 上一次都没跑过。另外这份文件必须先合进 +# main,cron 才会第一次响:合并之前一次都不会跑。 # ② 失败可见性与 push 一样:`::error::` 注解由 test 作业的 shell 步骤自己产出,不读 event 上下文; # 定时任务的失败同样会通知默认分支的维护者。 # ③ 逐个 job 核过 `github.event` 依赖:整个文件只有 runtime-smoke-macos 的 # `if: github.event_name == 'workflow_dispatch'` 看 event,在 schedule 下为假 ⇒ 该 job 跳过 # (它本来就只是手动探针,见其注释);其余 job 与矩阵里没有任何 event 表达式,夜间不会空转。 +# ④ 定时运行必须与 push main **分列到不同的 concurrency 组**:两者的 `github.ref` 都是 +# `refs/heads/main`,同组 + `cancel-in-progress` 会让它们互相取消 —— 而**取消不是红**。 +# 一个经常被静默取消的夜间入口比没有更坏:「昨晚没报错」会变成一句假话。所以 group 里带了 +# event_name(证据强度见 `concurrency:` 上方那几条注释,别当成已在 runner 上实证过)。 # -# `cron` 一律 UTC:`17 3 * * *` = 每天 03:17 UTC(UTC+8 的 11:17)。分钟刻意避开 :00 / :30 -# ——整点是定时队列最挤的时候。要挪到本地凌晨就把它改成 `17 19 * * *`,其余不用动。 +# 时间:`cron` 一律 UTC,所以这里写死 `17 19 * * *` = **本地(UTC+8)每天 03:17**。 +# 分钟刻意避开 :00 / :30——整点是 GitHub 定时队列最挤的时候。 on: push: branches: [main] pull_request: workflow_dispatch: - # 夜间全量矩阵(M266 / T30,理由与取舍见文件头)。cron 是 UTC;17 分避开整点队列。 + # 夜间全量矩阵(M266 / T30,理由与取舍见文件头):17:19 UTC = 本地 03:17。 schedule: - - cron: '17 3 * * *' + - cron: '17 19 * * *' env: CARGO_TERM_COLOR: always @@ -51,8 +56,10 @@ env: concurrency: # 加了 schedule 之后这一行必须带上 event_name:定时运行落在默认分支上,`github.ref` 同样是 # `refs/heads/main`,与「push main」落进同一个组 ⇒ 配上 cancel-in-progress 就是两者互相掐死 - # (夜里那轮被白天的推送取消 = 那一夜没有结果,正是本次要填的洞)。带事件名之后: + # (夜里那轮被白天的推送取消 = 那一夜没有结果,而取消不是红,正是本次要填的洞)。带事件名之后: # push↔push 仍按 ref 互斥(原有行为一字未变),定时自成一列,既不会被推送取消,也不去取消推送。 + # 证据强度:以上是 **GitHub 文档语义**(schedule 事件的 ref 是默认分支、取消按 group 判定), + # **没有在真实 runner 上构造并发验证过**——本地造不出两个并发 run。别把它当实测结论引用。 group: ci-${{ github.event_name }}-${{ github.ref }} cancel-in-progress: true diff --git a/docs/backend-cjk-evidence-matrix.md b/docs/backend-cjk-evidence-matrix.md index ccbbd00..1842fa1 100644 --- a/docs/backend-cjk-evidence-matrix.md +++ b/docs/backend-cjk-evidence-matrix.md @@ -7,11 +7,15 @@ | 通道 | 位置 | 后端 | |---|---|---| -| CI 冒烟截屏 | `.github/workflows/ci.yml:101` | `--software-renderer`(强制 CPU) | -| CI 交互冒烟 | `.github/workflows/ci.yml:246`、`:283` | `--software-renderer` | -| Linux 容器冒烟 | `packaging/linux-smoke-docker.sh:90` | `--software-renderer` | +| CI 冒烟截屏 | `.github/workflows/ci.yml` 的 `test` 作业「出四张像素守门用的图并写进 $GITHUB_ENV(仅 Windows)」那一步 | `--software-renderer`(强制 CPU) | +| CI 交互冒烟 | 同上文件的 `runtime-smoke` 作业「在 Xvfb 里跑一帧并截图」、`runtime-smoke-macos` 作业「尝试截图」两步 | `--software-renderer` | +| Linux 容器冒烟 | `packaging/linux-smoke-docker.sh` 里「2) 跑容器:构建 → xvfb 截图 → 像素判定」那一段的 `xvfb-run` | `--software-renderer` | | D30 视觉验收(人工,按协议出图) | `DevDocs/PlanDoc/`(视觉验收协议) | 同上(CI 同款命令) | +> **锚点一律用「作业名 + 步骤名」,不用 `文件:行号`。** 这三处原来写的是行号,已经腐过一次 +> (M266 往 ci.yml 文件头加夜间矩阵的注释之后,三处行号全部偏移)。workflow 只会越来越长, +> 行号锚点必然再腐;要定位就按上表的名字 `grep` 作业名 / 步骤名。 + `--renderer=auto`(默认)会挑 wgpu;`--software-renderer` 强制 tiny-skia。CI 走后者是**有意的**: 容器/无 GPU 机器上没有 Vulkan/DX12 设备,硬要 wgpu 会红在一句「adapter 创建失败」上,与皮肤无关。 From 8abe8c1fa32d2a93d606d1134c20df66b5fca3cb Mon Sep 17 00:00:00 2001 From: AskaEth Date: Wed, 11 Nov 2026 22:16:19 +0800 Subject: [PATCH 3/3] =?UTF-8?q?ci(m266):=20r3=20=E5=8F=AA=E4=BF=AE?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E7=9A=84=E4=BA=8B=E5=AE=9E=E6=80=A7=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=8A=A8=E8=A7=A6=E5=8F=91=E9=9D=A2=EF=BC=88=E5=A4=8D?= =?UTF-8?q?=E5=AE=A1=20p2-3items=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ci.yml: 换算口径统一成「19:17 UTC = 本地(UTC+8)次日 03:17」(cron 是 '17 19', 原先内联写 17:19 差 12 小时,同一文件两套换算互相打架),全文只留这一种写法 - ci.yml: 通知收件人按文档原话改(发给「最后修改 cron 语法的人」,不是「默认分支的维护者」) - ci.yml: 新增边界 ⑤ public 仓库 60 天无活动 scheduled workflow 自动 disable, 这是「夜里那轮某天不再响且没人发现」的真实失效模式 - ci.yml: 「:30 也挤」无文档依据,改成文档原话(整点高负载 + 负载过高时排队任务会被 dropped) - ci.yml: ③ 的 event 依赖断言改严:除 concurrency 组那一处与 runtime-smoke-macos 的 if 之外无 event 表达式 - docs: 归因改对——base(main) 上 :101 当时是对的,:246/:283 早就指偏(依赖安装步 / 一行注释), 行号锚点本来就在腐,不是本次改动造成 行为面零改动:on / concurrency / jobs 的键值行与 r2 逐字相同(本提交的 ci.yml 差异全部是注释行) --- .github/workflows/ci.yml | 27 ++++++++++++++++++--------- docs/backend-cjk-evidence-matrix.md | 7 ++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83ec0a5..8bc6635 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,30 +22,39 @@ name: CI # 这是**故意的取舍**:抓得住「平台回归 T+1 就能看见」,抓不住「当天的错当天见」。dev 线的快守门 # (fmt / clippy / guards / gpu 自测)在 dev-guards.yml,那边刻意不上全量 test / build,理由见其头部。 # -# schedule 的四条语义边界(省得下次重新查): +# schedule 的五条语义边界(省得下次重新查;每条都能在 GitHub 文档里对上原文): # ① 定时只在**默认分支**上生效:cron 跑的是 **main 的末梢**,dev 上尚未合入的内容**不在这一轮里**。 # 所以「dev 已经有人天天看着了」这句话是**错的**——dev 线只有 dev-guards.yml 那几道快守门 # (fmt / clippy / guards / gpu 自测),全量矩阵在 dev 上一次都没跑过。另外这份文件必须先合进 # main,cron 才会第一次响:合并之前一次都不会跑。 -# ② 失败可见性与 push 一样:`::error::` 注解由 test 作业的 shell 步骤自己产出,不读 event 上下文; -# 定时任务的失败同样会通知默认分支的维护者。 -# ③ 逐个 job 核过 `github.event` 依赖:整个文件只有 runtime-smoke-macos 的 -# `if: github.event_name == 'workflow_dispatch'` 看 event,在 schedule 下为假 ⇒ 该 job 跳过 -# (它本来就只是手动探针,见其注释);其余 job 与矩阵里没有任何 event 表达式,夜间不会空转。 +# ② 失败要向人喊话:`::error::` 注解由 test 作业的 shell 步骤自己产出,不读 event 上下文,这半条 +# 与 push 一样。但**收件人不是「默认分支的维护者」**——文档原话是 "Notifications for scheduled +# workflows are sent to the user who last modified the cron syntax in the workflow file": +# 谁最后一次改这行 `cron`,就一直收它的告警,所以这行 cron 的改动者要当成值班人。 +# ③ 逐个 job 核过 `github.event` 依赖:除 concurrency 组那一处(`github.event_name`)与 +# runtime-smoke-macos 的 `if: github.event_name == 'workflow_dispatch'`(schedule 下为假 ⇒ 跳过, +# 它本来就只是手动探针)之外,其余 job 与矩阵里**没有任何** event 表达式,夜间不会空转。 # ④ 定时运行必须与 push main **分列到不同的 concurrency 组**:两者的 `github.ref` 都是 # `refs/heads/main`,同组 + `cancel-in-progress` 会让它们互相取消 —— 而**取消不是红**。 # 一个经常被静默取消的夜间入口比没有更坏:「昨晚没报错」会变成一句假话。所以 group 里带了 # event_name(证据强度见 `concurrency:` 上方那几条注释,别当成已在 runner 上实证过)。 +# ⑤ **它自己会悄悄停**:public 仓库的 scheduled workflow 在仓库 60 天无活动后会被**自动 disable** +# (文档:"In a public repository, scheduled workflows are automatically disabled when no +# repository activity has occurred in 60 days")。这是「夜里那轮某天不再响、而且没人发现」的 +# 真实失效模式,比红更隐蔽;本仓若是 public 就适用,发现入口不跑了先看那条 disable 提示。 # -# 时间:`cron` 一律 UTC,所以这里写死 `17 19 * * *` = **本地(UTC+8)每天 03:17**。 -# 分钟刻意避开 :00 / :30——整点是 GitHub 定时队列最挤的时候。 +# 时间:`17 19 * * *`。GitHub 按 UTC 解释 cron(文档:"By default, scheduled workflows run in UTC"), +# 所以它是 **19:17 UTC = 本地(UTC+8)次日 03:17**,本文件不再出现第二种换算口径。 +# 为什么是 17 分:文档只说 "the start of every hour" 是高负载期,且 "If the load is sufficiently high +# enough, some queued jobs may be dropped"(负载过高时排队任务会被**丢弃**,不只是延迟),并建议错开 +# "a different time of the hour"。文档没有「:30 也挤」这条依据,所以这里只避开整点。 on: push: branches: [main] pull_request: workflow_dispatch: - # 夜间全量矩阵(M266 / T30,理由与取舍见文件头):17:19 UTC = 本地 03:17。 + # 夜间全量矩阵(M266 / T30,理由与取舍见文件头):19:17 UTC = 本地次日 03:17。 schedule: - cron: '17 19 * * *' diff --git a/docs/backend-cjk-evidence-matrix.md b/docs/backend-cjk-evidence-matrix.md index 1842fa1..4973b21 100644 --- a/docs/backend-cjk-evidence-matrix.md +++ b/docs/backend-cjk-evidence-matrix.md @@ -12,9 +12,10 @@ | Linux 容器冒烟 | `packaging/linux-smoke-docker.sh` 里「2) 跑容器:构建 → xvfb 截图 → 像素判定」那一段的 `xvfb-run` | `--software-renderer` | | D30 视觉验收(人工,按协议出图) | `DevDocs/PlanDoc/`(视觉验收协议) | 同上(CI 同款命令) | -> **锚点一律用「作业名 + 步骤名」,不用 `文件:行号`。** 这三处原来写的是行号,已经腐过一次 -> (M266 往 ci.yml 文件头加夜间矩阵的注释之后,三处行号全部偏移)。workflow 只会越来越长, -> 行号锚点必然再腐;要定位就按上表的名字 `grep` 作业名 / 步骤名。 +> **锚点一律用「作业名 + 步骤名」,不用 `文件:行号`。** 这条规则不是本次改动造出来的:本次改动的 +> base(`main`)上,上面 ci.yml 那三处行号里只有 `:101` 对得上,`:246` / `:283` 早就指偏了(分别落在 +> 依赖安装步和一行注释上;`linux-smoke-docker.sh:90` 那时是对的)。也就是说行号锚点本来就在腐, +> 而 workflow 只会越来越长——要定位就按上表的名字 `grep` 作业名 / 步骤名,别再写回行号。 `--renderer=auto`(默认)会挑 wgpu;`--software-renderer` 强制 tiny-skia。CI 走后者是**有意的**: 容器/无 GPU 机器上没有 Vulkan/DX12 设备,硬要 wgpu 会红在一句「adapter 创建失败」上,与皮肤无关。