diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md index 3e4954f1..6dff6ddb 100644 --- a/.dsh/skills/lvgl-simulator/SKILL.md +++ b/.dsh/skills/lvgl-simulator/SKILL.md @@ -46,7 +46,7 @@ RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/ch | 现象 | 根因 | |---|---| -| 找不到 `SDL2/SDL.h` | LVGL SDL 驱动写死该路径,头文件须有 `include/SDL2/` 副本(仓库已内置) | +| 找不到 `SDL2/SDL.h` | LVGL SDL 驱动写死该路径,须有 `include/SDL2/` 副本。⚠ **这份 SDK 不入库**(`.gitignore` 排除 `simulator/SDL2/`,实测 `git ls-files` 在该路径命中 0 个文件)——"下载才有"不等于"仓库已内置";CI/runner 的干净检出里没有它,2026-10-27 run 45 就在这里红。CI 侧用机器级 `DTU_SDL2_DIR` 指一份 SDK,见 `docs/CI/远程CI完整指南.md` §4.5 | | 找不到 `lv_conf.h` | `LV_CONF_DIR`/`SDL2_INCLUDE_DIR` 必须在 `add_subdirectory(lvgl)` **之前**定义 | | 窗口全黑 | mono 主题未生效 / 缺 `lv_refr_now(NULL)` / 重复喂 tick(SDL 驱动内部已 `lv_tick_set_cb`) | | 画面正常但按键全无效 | 主循环**不得**调 `SDL_PollEvent`——LVGL SDL 驱动内部 5ms 定时器统一分发,先轮询会抢光事件 | diff --git a/.dsh/skills/remote-ci-ops/SKILL.md b/.dsh/skills/remote-ci-ops/SKILL.md index 42509b3b..ba3f4647 100644 --- a/.dsh/skills/remote-ci-ops/SKILL.md +++ b/.dsh/skills/remote-ci-ops/SKILL.md @@ -39,7 +39,7 @@ whenToUse: CI 没跑、run 被取消或零日志、页面报的失败时长/红 | 类 | 判据(日志里的证据行) | 结论与动作 | |---|---|---| | **A. job 没跑起来** | checkout 步骤报拉不到 `actions/checkout` / 连 `github.com` 超时,**通篇零钩子输出** | 门禁内容无罪。内网无公网时的三种处置在 `docs/CI/远程CI完整指南.md` §5.3(推荐零依赖纯 git checkout)。**先修它再谈别的**,否则后面所有"红"都不可读 | -| **B. runner 机器缺环境** | `::warning::runner 未安装 MSVC`、`no Python with a SYSTEM-visible pre-commit`、`runner missing cppcheck`、`test-coverage` 因缺 **mscoverage** 组件而 FAIL(按用户裁定缺件即 FAIL,不 skip) | 也是"非代码问题",但**别改成 skip 蒙过去**:按`docs/CI/远程CI完整指南.md` §4.1/§4.2 补机器级工具链或 `DTU_*`,改完重启 runner | +| **B. runner 机器缺环境** | `::warning::runner 未安装 MSVC`、`no Python with a SYSTEM-visible pre-commit`、`runner missing cppcheck`、`test-coverage` 因缺 **mscoverage** 组件而 FAIL(按用户裁定缺件即 FAIL,不 skip)、`ui-golden` 因缺 **SDL2 SDK** 而 FAIL(该 SDK 不入库,见指南 §4.5) | 也是"非代码问题",但**别改成 skip 蒙过去**:按`docs/CI/远程CI完整指南.md` §4.1/§4.2 补机器级工具链或 `DTU_*`,改完重启 runner | | **C. 真门禁失败** | 钩子清单完整且 `failed>0` 或某条 `Failed`,**且 §5 的本地同命令复现同样的红** | 这才轮到读 workflow / 门禁脚本,按失败钩子逐条定位 | **顺序不能反**:A/B 两类在本地都是"全绿",用"本地绿 + CI 红"直接推"环境问题"也推不出是 A 还是 B——只有钩子输出能把它们分开。本轮实测:页面报的红看着像门禁失败,实际是 A(checkout 联网超时),而 A 修好之后 L0 还会再红两次——一条 C、两条 B。 @@ -51,6 +51,7 @@ whenToUse: CI 没跑、run 被取消或零日志、页面报的失败时长/红 | Actions 页空白、无 run | 看改了啥 + 怎么推的 | 四因:①服务端开关未开 ②workflow 没推到远程 ③**纯文档**(`paths-ignore: docs/**`、`**/*.md`)④**新建分支的首次 push**——`before` 是全零 SHA,变更文件集算不出来被 `paths-ignore` 滤空。④ 的实测:建分支那次 push 无 run,紧接着一次普通 push 立刻出 task;绕法 `workflow_dispatch` 或再推一个非文档提交 | | **页面报的时长/红数与本地或日志对不上** | §4.3 查 `action_run`/`action_run_job` 的 created/started/stopped | **页面时长不可照抄**。本轮实测页面写 `Failing after 6m49s`,库里 41 次 run **最长 ≈4m33s**,run 41 总时长 95 s(L0 45 s / L0.5 43 s)——**没有任何一条记录对得上 6m49s**(多半掺了排队/重试/页面自身口径)。以库里的 `total=stopped-created`、`run=stopped-started`、`queue=started-created` 三个数为准 | | run 全 **cancelled**、零日志 | `& '.\scripts\ci_runner_bootstrap.ps1' -Status` | runner 掉线;且 `ci.yml` 的 `concurrency.cancel-in-progress: true` 会把排队 run 被下次 push 取消 → **症状是"任务消失"而不是"任务失败"**(历史上 16–21 连续 12 个 job 零日志即此) | +| `ui-golden` 红、日志是 `C1083: SDL2/SDL.h` 或 `MSB1009: 项目文件不存在`,本地全绿 | `git ls-files` 在 `simulator/SDL2*` 下命中 0(SDK 不入库);或 `simulator/build/CMakeCache.txt` 在但没有 `ALL_BUILD.vcxproj` | 属 **B 类缺环境**:设机器级 `DTU_SDL2_DIR`(指南 §4.5);毒缓存已由 `build_sim.ps1` 自愈,也可手删 `simulator/build` | | runner 显示 **online** 却每 10 秒重启、日志暴涨(实测 ~20 MB/天) | daemon 日志出现 `another gitea-runner process is already using ".runner"` | **双看门狗抢锁**:`Unregister-ScheduledTask` **不会**停掉旧实例起的进程 → 孤儿看门狗养着旧 daemon → 它持 `.runner.lock` → 新任务里的 daemon 起不来,而**服务端仍显示 online**(旧 daemon 在服务)。修:管理员 `-StartNow`(它**先杀看门狗、再杀 daemon**,顺序反了会被 10 秒内复活) | | L0 失败但**本地同一条命令全绿**,日志 `exit code: 254` + `Missing git configuration: please set user.name` | 看是不是 254 | **不是提交信息问题**:`actions/checkout` 把 HOME 指向每次任务的临时目录,SYSTEM 无全局 git 身份 → gitlint 在读规则**之前**就配置错误退出。已由 `scripts/ci_gitlint_range.ps1` 用 `GIT_CONFIG_*` 注入一次性身份修掉;**254 = 门禁没跑起来(exit 2),1 = 信息真违规(exit 1)**,别把两者混成一句 | | 钩子条数对不上 | `grep -c 'id:' .pre-commit-config.yaml` | **报告数 = 定义数 − 1**(gitlint 属 `commit-msg` stage,`--all-files` 跑不到且表现为全绿 = 静默空转)。增删钩子后重算这个差值 | @@ -140,6 +141,7 @@ cur.execute("SHOW COLUMNS FROM action_run_job") # 🔴 列名以这里为准 | 1 | Success | 同上 | | 2 | **Failure** | run 41 = 2 → Failure,与页面红、与 runner 日志 `task finished: failure` 三方一致 | | ≥3 | **本轮未核实** | 试错时猜过 `3=Failure`/`4=Cancelled`/`5=Skipped`,**都没有交叉验证** → 用到之前先拿一条已知结局的 run 反推(TODO:从 Gitea 源码定死,别再靠猜) | +| 5 / 6 | **非终态**(排队中 / 运行中) | 观测于 run 45:推送后先看到 job=5(未开始)与 6(进行中),数分钟后转 1 / 2;**别把它当成库里坏了或卡死** | - **写查询时同时打印原始数字和映射名**;`action_run` 与 `action_run_job` **默认按同一套码处理是危险假设**,跨到别的表要重验。 - 判据:同一个 run 换一套映射后红绿翻转(本轮 step 53 判 `Success`、step 56 判 `Failure`)→ 说明你还没定死枚举,**这时任何"红绿史"都不许出口**。 diff --git a/docs/CI/README.md b/docs/CI/README.md index ee356bdc..153015f5 100644 --- a/docs/CI/README.md +++ b/docs/CI/README.md @@ -87,6 +87,7 @@ | 日期 | 变更 | |---|---| | 2026-10-27 | run 44(推送到已存在分支会正常建 run)暴露的教训写进指南 §5.3:run: 块是被**点源**执行的,`$ErrorActionPreference` 继承 Stop 时原生 stderr 会终止整步;且测 run: 块只能用 `-Command ". 文件"`(`-File` 包点源会把失败测成 rc=0)。§7 补「几秒就亡」一行,skill §10 补测法 | +| 2026-10-27 | run 45 首次真跑门禁(L0 46 根绿、L0.5 3188/3188);唯一红 `ui-golden` 定性为 **runner 缺环境**:SDL2 SDK 不入库。新增指南 §4.5:机器级 `DTU_SDL2_DIR` + CMake 配置期硬校验 + `Find-Sdl2Sdk` + `build_sim.ps1` 毒缓存自愈;§4.1.3 变量表补到 5 个、§5.5 与 §7 各补一处;顺带更正 lvgl-simulator skill 的「SDL2 已内置」说法 | | 2026-10-27 | 指南 §5.3 **整节重写并落地**:workflow 两个 job 的 `uses:` 归零(原生 git 全量检出,六项实测 A/B/C/D/E/F + 两条自查命令见 §5.3);§1.2.1 第三行按 1.25.4 实际行为改正(自定义 `DEFAULT_ACTIONS_URL` 已无效,旧写法会把人引向一个不存在的开关);§7 的 checkout 行改为“看见这症状=有人把 `uses:` 加回来了”。另:`scripts/ci_runner_bootstrap.ps1` 的 MSVC 探测改为 `vswhere` + 工具集钉世代(默认 14.51,与覆盖率基线同版本),`-SetEnv` 开始成对写 `DTU_MS_COVERAGE` | | 2026-10-27 | 新增指南 §4.4「覆盖率工具链(mscoverage)与 VS2026 BuildTools」并加进上面的目录表。当天另修掉 `cppcheck` 两根真红(`ci/remote-ci` 的 `5869c03`)——此前 L0 的 28 次红里混着**三类**:runner 缺覆盖率组件、外网拉 `actions/checkout` 抖动、真代码问题。**别再一律当成环境噪声**,按 §7 逐条定性 | | 2026-10-12 | 🐛 **首次真跑出 run**:`L0.5` **成功**(`10 runner(s), 647/647, fail=0`,与本地逐字一致);`L0` **假失败**——gitlint 因 runner 无 git 身份而 `exit 254`(`Missing git configuration: please set user.name`),旧文案把它说成"提交信息违规",且该步 `exit 1` 连带让 26 个钩子全部没跑。已在 `ci_gitlint_range.ps1` 用 `GIT_CONFIG_*` 注入一次性身份(不写文件)并区分 254/1 退出码。另记:**新建分支的首次 push 不触发 run**,需 `workflow_dispatch` 或再推一个非文档提交。详见指南 §7 | diff --git a/docs/CI/远程CI完整指南.md b/docs/CI/远程CI完整指南.md index 55c7fdf7..e1fa6931 100644 --- a/docs/CI/远程CI完整指南.md +++ b/docs/CI/远程CI完整指南.md @@ -832,13 +832,15 @@ mv .dsh/skills/serial-assistant/scripts/state.bak_ci .dsh/skills/serial-assistan #### 4.1.3 机器级 `DTU_*` 环境变量(runner 的正式覆盖通道,2026-10-02 起) workflow 已改为统一消费 `DTU_*` 接口(见 [本地工具路径环境变量方案.md §3.3](../环境与部署/本地工具路径环境变量方案.md)): -**显式变量优先 → 自动发现回退**,三个 job 步骤相关的变量: +**显式变量优先 → 自动发现回退**,与 job 步骤相关的变量(2026-10-27 实测 `-Status` 列 5 个): | 变量 | 作用 | 未设置时的回退 | |---|---|---| | `DTU_PRECOMMIT_PYTHON` | 指向装有 pre-commit 的 python.exe(L0/L0.5 用) | PATH 的 `python` → 工作区 `.venv`(干净克隆里通常没有) | | `DTU_CPPCHECK` | 指向 cppcheck.exe(L0 钩子用) | SYSTEM 的 PATH(用户级 PATH 不可见) | | `DTU_MSVC_CL` | 指向 x64 `cl.exe`(L0.5 用) | `%ProgramFiles%`/`%ProgramFiles(x86)%` 推导的 VS 标准根自动探测 | +| `DTU_MS_COVERAGE` | 指向 `Microsoft.CodeCoverage.Console.exe`(`test-coverage` 钩子用) | 顺 `DTU_MSVC_CL` 所在的那套 VS 根自动发现(§4.4) | +| `DTU_SDL2_DIR` | 指向一份 SDL2 SDK 根(须含 `include\SDL2\SDL.h`;`ui-golden` 要编模拟器) | `simulator\SDL2\SDL2-*`——**干净检出里根本没有这份 SDK,CI 必红**(§4.5) | 设置(**必须管理员**;scope 用 `Machine` 才能被 SYSTEM 任务看到): @@ -966,6 +968,55 @@ MSVC 114 行),**换编译器世代 = 换分母**;指错版本只会把「E 安装,非提权读不到该账户 HKCU,**未证**)。→ 「CI runner 与本机同一台」这句要按**量基线那台可能是另一台 机器**来理解;换机或换账户一律先 `check_test_coverage.py --list` 再跑全量。 +### 4.5 UI 像素回归的 SDL2 SDK:CI 工作区**天然没有**(2026-10-27 run 45 实测) + +`ui-golden` 要先把 LVGL 模拟器编出来,而模拟器依赖 **SDL2 SDK**。这份 SDK 是**下载来的第三方件**: +`.gitignore` 里明写 `simulator/SDL2/`,`git ls-files` 在该路径下命中 **0 个文件**。于是任何一份干净 +检出里都没有它,而开发机上有——所以本地一直绿,**这个缺失在本地永远照不出来**。 + +run 45 第一次在 CI 跑到 `ui-golden` 时报的是: + +``` +error C1083: 无法打开包括文件: "SDL2/SDL.h" + (Lib/lvgl/src/drivers/sdl/lv_sdl_private.h:20) +``` + +⚠ **别把它读成「UI 像素回归红了」**:这是红因三分法里的**第二类(runner 缺环境)**,与像素、与 +被检的提交内容都无关。配置阶段那句 `-- SDL2: <路径>` 也**不能**当证据——`simulator/CMakeLists.txt` +过去只是 `set()` 拼路径、不校验存在,缺件时照样打印,直到编译 LVGL 的 SDL 驱动才炸。 + +**已落地的修法(四件)**: + +1. `simulator/CMakeLists.txt` 支持机器级 **`DTU_SDL2_DIR`** 覆盖 SDK 位置;**不设该变量时行为与 + 过去完全一致**(实测:不设 → 取 `simulator/CMakeLists.txt` 里写死的仓内默认路径 → `ui-golden Passed`)。 +2. 同文件加**配置期硬校验**:缺 SDK 时一行 `FATAL_ERROR` 讲清三条出路(本机装 / CI 设变量 / + 删旧 build 缓存),而不是几百行 C1083 冒充代码错误。实测护栏生效:改前那种工作区里 + `C1083` 行数从一整屏降到 **0**,只剩护栏自己那一条。 +3. `scripts/ci_runner_bootstrap.ps1` 新增 `Find-Sdl2Sdk`,顺序:已有 `DTU_SDL2_DIR`(Machine → + User → Process,逐个校验含 `include\SDL2\SDL.h`)→ 机器级 `D:\DTU_SDK\SDL2-*` → 本仓 + `simulator/SDL2/SDL2-*`;**命中第 3 档会 WARN**「runner 工作区里不会有这个路径」。`-SetEnv` + 写入该变量,`-Status` 一并列出。 +4. **SDK 放机器级而不是仓内**:本机已把那份 26 MB 的 SDK 整目录拷到 `D:\DTU_SDK\` 下(子目录名 + 就是 SDK 自带的版本号目录名)。`Find-Sdl2Sdk` 按 `SDL2-*` 通配发现、**不写死版本**,所以文档 + 里不钉那个目录名(它形如编号,会撞进 `id-check` 校验 8 的噪声桶,而那条上限只许减不许增)。 + 同理也别把变量指回某人的开发检出——CI 的工作区是 runner 自己的目录,不是你这份仓。 + +⚠ **第二个坑(毒缓存):设好变量仍然红,红因已经不是 SDK**。上一次配置在 `FATAL_ERROR` 处中断 +时,CMake **已经把 `CMakeCache.txt` 写下**、工程文件却没生成;`build_sim.ps1` 的判断是「有缓存就 +不再配置」,于是下一轮**跳过配置**直接进 MSBuild,报 `MSB1009: 项目文件不存在`——离根因十万八千里。 +更麻烦的是 `simulator/build/` 属被忽略文件,而检出用的是 `git clean -ffd`(**刻意保留**被忽略文件, +见 §5.3),**这份毒缓存会跨 run 存活**。已在 `build_sim.ps1` 第三节补一条同形检查(那里原本就有 +「生成器不符 → 删 build/ 重来」的先例):有缓存但没有 `ALL_BUILD.vcxproj` → 删除重建。 + +实测闭环(在一份**没有**仓内 SDK、且带着 run 45 毒缓存的检出里): + +``` +ws cache present=True ALL_BUILD present=False <- 毒化状态,正是 CI 现在的处境 +[RECOVERY] rc=0 UI 像素回归(菜单 8 状态 + 内容页 40 页 黄金参考)....Passed +``` + +即:只需管理员跑一次 `-SetEnv`(与 §4.4 的三条命令同批),CI 的 `ui-golden` 就会转绿, +且**自愈** run 45 已经污染的那份工作区。 --- ## 5. 仓库端:workflow 文件 @@ -1129,7 +1180,7 @@ Select-String -Path '.gitea/workflows/ci.yml' -Pattern '^\s*-?\s*uses:' | job | `runs-on` | 步骤要点 | |---|---|---| -| `l0-gates` | `windows` | **原生 git 检出**(全量 refspec,不再用 `actions/checkout`;见 §5.3)→ 环境自检检(**Python 探测走 `scripts/ci_find_python.ps1`**、cppcheck 按 `DTU_CPPCHECK` → PATH → 标准安装目录,缺则 `::error::` + exit 1)→ `pre_commit install --install-hooks` → **提交信息门禁 `scripts/ci_gitlint_range.ps1`**(见 §7 排障表"钩子条数对不上"行)→ **决定跳过哪些钩子**(探测 `.map`,写入 `GITHUB_OUTPUT`;无 `.map` 时 `::warning::` 声明体积门禁未跑)→ `pre-commit run --all-files --show-diff-on-failure`(`env: SKIP`,输出 `Tee` 到 `ci-artifacts/l0-pre-commit.log`)→ 打印 `::notice::L0: passed/failed/skipped` 汇总行 → 归档日志说明 | +| `l0-gates` | `windows` | **原生 git 检出**(全量 refspec,不再用 `actions/checkout`;见 §5.3)→ 环境自检检(**Python 探测走 `scripts/ci_find_python.ps1`**、cppcheck 按 `DTU_CPPCHECK` → PATH → 标准安装目录,缺则 `::error::` + exit 1)→ `pre_commit install --install-hooks` → **提交信息门禁 `scripts/ci_gitlint_range.ps1`**(见 §7 排障表"钩子条数对不上"行)→ **决定跳过哪些钩子**(探测 `.map`,写入 `GITHUB_OUTPUT`;无 `.map` 时 `::warning::` 声明体积门禁未跑)→ `pre-commit run --all-files --show-diff-on-failure`(`env: SKIP`,输出 `Tee` 到 `ci-artifacts/l0-pre-commit.log`)→ 打印 `::notice::L0: passed/failed/skipped` 汇总行 → 归档日志说明;**`ui-golden` 需要 SDL2 SDK,而该 SDK 不入库** → 依赖机器级 `DTU_SDL2_DIR`(缺件即 FAIL,护栏与落地见 §4.5) | | `l05-host-unit-tests` | `windows` | **原生 git 检出**(与 `l0-gates` 第 1 步逐字一致,内联带来的同步义务,自查见 §5.3 末)→ **探测 MSVC**MSVC**(按 VS_ROOTS 找 `Hostx64\x64\cl.exe`)→ 遍历 `test/host/**/run_unit_test.py` 逐个跑(输出 `Tee` 到 `ci-artifacts/l05-<模块>.log`;解析各模块 `N/M PASSED` 汇总行 → 聚合 `::notice::L0.5: K runner(s), A/B assertions passed`;失败 `::error::` 并置退出码);**未探测到 MSVC 时打印醒目 WARNING 并降级跳过**(不硬失败,避免"没装 VS 的 runner 全红") | > 🔴 **两条 Python 探测链必须同序同步**:本地 `scripts/ci_local.py` 与 CI 侧 @@ -1207,6 +1258,7 @@ date +%s # 对比 last_online | `l0-gates` 报 `::error::runner 缺少:Python, cppcheck` | runner 没装或不在 PATH | 装完后**重启 runner 服务**(新进程才吃到新 PATH) | | `Prepare all required actions` 或 `checkout` 卡住/超时(~45 s 后整 job 亡、零钩子输出) | 有人把 `uses:` 加回了 workflow —— 该阶段**先于任何步骤**无条件联网,手工 curl 可达不代表 runner 可达 | 本仓库 2026-10-27 起已无 `uses:`(§5.3 有六项实测与自查命令);自查:`Select-String .gitea/workflows/ci.yml -Pattern '^\s*-?\s*uses:'` 应为空 | | job **3~5 秒就亡**、日志里是 `CategoryInfo` + `FullyQualifiedErrorId : NativeCommandError`,而同一条命令在本地全绿 | `run:` 块被**点源**进会话且 `$ErrorActionPreference` 继承为 Stop → 原生程序的 stderr 行变成**终止性**错误(`2>$null` 挡不住) | 块首显式设 `Continue`,原生调用一律摊平(`2>&1` 配 `ForEach-Object` 那类漏斗);复现方法见 §5.3 的 run 44 实录 | +| `ui-golden` 红、日志里是 `C1083: SDL2/SDL.h` 或 `MSB1009: 项目文件不存在`,而本地全绿 | 前者 = 检出里根本没有 SDL2 SDK(`.gitignore` 排除、不入库);后者 = 上一轮配置中断留下的半截 `simulator/build` 缓存跨 run 存活 | 设机器级 `DTU_SDL2_DIR`(§4.5);缓存那条已在 `build_sim.ps1` 自愈,也可手删 `simulator/build` | | `l05` 日志出现 `::warning::runner 未安装 MSVC` | VS 装在非默认路径或未装 | 见 §4.2(默认路径 / 同步改两处根目录) | | `mem-numbers` 显示 `Skipped` | 正常(CI 无 Keil,无 `.map`) | 无需处理;但注意 **CI 未守体积门禁**(见 §0.0),改代码后本地 `@build` + `check_mem_numbers.py --scan` 兜底 | | L0.5 显示 `2 runner(s), X/Y ...` 但 X.log` | @@ -1499,6 +1551,7 @@ powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -DryRun | 日期 | 变更 | |---|---| +| 2026-10-27 | 🟡 **run 45 真把门禁跑起来了**:L0 46 根钩子 Passed / L0.5 = 40 runner 3188/3188 fail=0(`checkout ok: ae80bb8 / commits=670`,SYSTEM 读到机器级 `DTU_MSVC_CL` = E 盘 14.51)。唯一红 = `ui-golden`,定性为**第二类 runner 缺环境**而非像素回归:SDL2 SDK 被 `.gitignore` 排除、不入库,干净检出天然没有(本地有 → 一直绿)。新增 §4.5:`CMakeLists.txt` 支持机器级 `DTU_SDL2_DIR`(不设=行为不变,实测 Passed)+ 配置期硬校验(护栏把一整屏 C1083 降到 0 行)+ `Find-Sdl2Sdk` 三级发现(机器级 `D:\DTU_SDK` 优先,命中仓内路径会 WARN)+ **毒缓存坑**:配置中断留下 `CMakeCache.txt` 却没有工程文件,而 `build_sim.ps1` 是「有缓存就不再配置」→ 下一轮跳过配置、报离根因极远的 `MSB1009`,且 `simulator/build/` 被 `git clean -ffd` 刻意保留 → **跨 run 存活**;已按该文件既有先例补「有缓存无工程 → 删重」。实测闭环:在一份没有仓内 SDK 且带毒缓存的等价工作区里,仅设变量即 `Passed`(8+40 全过、自愈)。§4.1.3 变量表由 3 个补到 5 个;§5.5 的 `l0-gates` 步骤要点补上 `ui-golden` 的 SDK 依赖;skill 侧补症状行、三分法 B 类例、`run.status` 5/6 为非终态;顺带更正 lvgl-simulator skill 中「SDL2 仓库已内置」的说法 | | 2026-10-27 | 🔴 **run 44 真机首验失败一次 → 两条「本地照不出来」的教训写进 §5.3**:把原生检出差上去推到 `ci/remote-ci`(10:38)后两个 job 分别 3 s / 2 s 亡,日志是 `error: No such remote 'origin'` 被包成 `NativeCommandError` 终止整步。教训①:runner 用 `powershell -command . '<文件>'` **点源**执行 run: 块,会话继承 `$ErrorActionPreference=Stop` 时原生程序的 stderr 一行就是**终止性错误**,`2>` 重定向挡不住——修法:块首显式 `Continue` + 所有 git 调用走摊平漏斗(另把 `git remote get-url` 换成 `git config --get`,缺 key 时不写 stderr)。教训②:**测法必须复现点源**——实测 `exit 1` 语义矩阵:`-File` 直跑=1 / `-File` 包点源=**0 且继续执行** / `-Command` 点源=1;我第一版复现器用了中间那种,把四条失败路径全测成 rc=0(假警报,也照不出教训①)。换点源测法后:全新工作区 150 s `checkout ok: c1e7e8c / commits=669`、四条失败路径 ExitCode 全 1、复用工作区 1.6 s 且 gitignored 文件保留。§5.3 实测表补 G/H 两行、§7 补「几秒就亡」一行、`remote-ci-ops` skill §10 补测法。另记:本仓 push 到**已存在**的远端分支会正常建 run(run 44),而新建分支首推不建(已三次复现) | | 2026-10-27 | ✂️ **CI 侧两处根治 + 更正我上一条的一处推断**:① **workflow 去掉 `uses:`**——两个 job 的第 1 步换成原生 git 全量检出(完整 refspec + 分离 HEAD + `reset --hard` + `clean -ffd`、令牌只走 per-invocation `-c http.extraheader`、`GIT_TERMINAL_PROMPT=0` 防挂死、目标 sha 不在历史里就 `::error::` 硬失败而不是默默构建错树)。动因是 run 36/41/42 三次 **`Prepare all required actions` 阶段 ~45 s 外网超时把整个 job 打死、零钩子输出**,而同一分钟手工 `curl` 返回 200。§5.3 整节重写,含六项实测(A/B/C/D/E/F——关键是 E:**把正文从 `ci.yml` 里抽出来跑**,测的就是文件里那份字节)与两条自查命令;§7 的 checkout 行同步改为“看见此症状=有人把 `uses:` 加回来了”。② **`scripts/ci_runner_bootstrap.ps1` 的 `Find-MsvcCl` 重写**:`vswhere -all -products *` 取实例(**能看见非默认盘**)+ 工具集版本降序 + `-MsvcToolset` 前缀**钉住世代**(默认 `14.51.`,与覆盖率基线同版本;钉不上连 WARN 再回落最新,绝不静默)+ 新增 `-MsvcLatest`;`-SetEnv` 增写 `DTU_MS_COVERAGE`(须与 `DTU_MSVC_CL` 成对改)。③ **更正**:先前写“旧探测会把机器级变量覆回 C 盘 14.29”——本机实情是 C 盘 VS2019 已于 **01:00 卸载**(`…\2019` 只剩空目录、C 盘 x64 `cl.exe` 实测 0 个),旧 `-SetEnv` 在本机是**找不到 → 只 Warn → 不写**,于是变量一直指着失效路径(CI 的 L0.5 走显式通道即硬失败,不做静默回退)。实测新逻辑:候选 3 个(14.44/14.51/14.52,全在 E 盘)→ 默认命中 **14.51.36231**,`-Latest` 给 14.52,`-MsvcToolset 99.` 走告警回落。④ §1.2.1 表第三行按 1.25.4 实际行为改正(自定义 `DEFAULT_ACTIONS_URL` 取值已无效)——旧写法会把下一个排障的人引向一个不存在的开关 | | 2026-10-27 | 🧯 **拆穿「L0 恒红 = 环境噪声」这个想当然 + 覆盖率工具链补齐**:① `cppcheck` 恒红**不是**版本漂移——本机与 runner 同机、同 exe(2.21.0)、同参数复现即红,两根 `uninitializedVariable`(`s_cmd_con` 在空信息体分支把未初始化 `body` 传下去、`s_spont_step` 的 `blen` 作用域过宽)是真代码问题,已在 `ci/remote-ci` 的 `5869c03` 修掉(`@build` 0/0,`Code 231712→231732`)。② 新增 §4.4:`Microsoft.CodeCoverage.Console.exe` **只随 VS 分发**(NuGet 两候选包实测 .exe 数为 0 / 只有老版 `CodeCoverage.exe`),本机补装 VS2026 BuildTools 18.11 于 **E 盘**后仍因非默认盘不被看见,须显式机器级 `DTU_MSVC_CL`(指 14.51 = 与 `scripts/test_coverage_baseline.json` 同版本);三条管理员命令、`-Force` 陷阱、`Restart-Service Schedule` 判据一并写入。③ 排障教训:**页面时长与本地对不上时以 `commit_status` + runner 日志为准**——页面那条「L0 Failing after 6m49s」在库里是 run 26 的 409 s(cppcheck 真红),同一 sha 另有一次 5 s 的 checkout 超时,**两者不是一回事**,别拿一个解释另一个 | diff --git a/scripts/ci_runner_bootstrap.ps1 b/scripts/ci_runner_bootstrap.ps1 index 9e378a15..0f68eec9 100644 --- a/scripts/ci_runner_bootstrap.ps1 +++ b/scripts/ci_runner_bootstrap.ps1 @@ -49,7 +49,7 @@ .PARAMETER SetEnv Discover and write MACHINE-level DTU_PRECOMMIT_PYTHON / DTU_CPPCHECK / - DTU_MSVC_CL / DTU_MS_COVERAGE. Existing values are preserved unless + DTU_MSVC_CL / DTU_MS_COVERAGE / DTU_SDL2_DIR. Existing values are preserved unless -Force. MSVC discovery goes through vswhere (so VS on a non-default drive is found) and pins the toolset with -MsvcToolset, because the coverage baseline was recorded with one specific MSVC version -- see @@ -245,6 +245,40 @@ function Find-MsvcCl { return $sorted[0].Cl } +# --------------------------------------------------------------------------- +# Locate an SDL2 SDK root usable by the UI simulator (ui-golden gate). +# The SDK is DOWNLOADED, not tracked: .gitignore excludes simulator/SDL2/, so a +# CI workspace never contains it. Measured 2026-10-27 run 45: ui-golden died with +# C1083 "SDL2/SDL.h" on the runner while every local run was green -- a missing +# third-party SDK, not a pixel regression. Discovery order: +# 1) an already-valid DTU_SDL2_DIR (Machine, then Process) +# 2) a machine-wide copy under -MachineSdkRoot (default D:\DTU_SDK) +# 3) this repository checkout's simulator/SDL2/SDL2-* -- works for local runs, +# but a runner workspace will not contain it, so on the CI box prefer 1 or 2 +# Returns the root that contains include\SDL2\SDL.h, else $null. +# --------------------------------------------------------------------------- +function Find-Sdl2Sdk { + param([string]$MachineSdkRoot = 'D:\DTU_SDK') + $rel = 'include\SDL2\SDL.h' + $cand = [System.Collections.Generic.List[string]]::new() + foreach ($sc in @('Machine', 'User', 'Process')) { + $v = [Environment]::GetEnvironmentVariable('DTU_SDL2_DIR', $sc) + if ($v) { $cand.Add($v) } + } + if ($MachineSdkRoot -and (Test-Path -LiteralPath $MachineSdkRoot)) { + Get-ChildItem -LiteralPath $MachineSdkRoot -Directory -Filter 'SDL2-*' -ErrorAction SilentlyContinue | + Sort-Object Name -Descending | ForEach-Object { $cand.Add($_.FullName) } + } + $repoSdk = Join-Path (Split-Path -Parent $PSScriptRoot) 'simulator\SDL2' + if (Test-Path -LiteralPath $repoSdk) { + Get-ChildItem -LiteralPath $repoSdk -Directory -Filter 'SDL2-*' -ErrorAction SilentlyContinue | + Sort-Object Name -Descending | ForEach-Object { $cand.Add($_.FullName) } + } + foreach ($c in $cand) { + if ($c -and (Test-Path -LiteralPath (Join-Path $c $rel))) { return $c } + } + return $null +} function Find-CoverageConsole { # Microsoft.CodeCoverage.Console.exe ships ONLY inside Visual Studio # (measured 2026-10-27: NuGet candidates contain no such .exe, or only the @@ -380,10 +414,10 @@ if ($Status) { if (Test-Gitea $GiteaUrl) { Ok "Gitea reachable at $GiteaUrl" } else { Fail "Gitea NOT reachable at $GiteaUrl" } - foreach ($n in @('DTU_PRECOMMIT_PYTHON', 'DTU_CPPCHECK', 'DTU_MSVC_CL', 'DTU_MS_COVERAGE')) { + foreach ($n in @('DTU_PRECOMMIT_PYTHON', 'DTU_CPPCHECK', 'DTU_MSVC_CL', 'DTU_MS_COVERAGE', 'DTU_SDL2_DIR')) { $v = Get-MachineEnv $n if ($v) { Ok ("MACHINE {0} = {1}" -f $n, $v) } - else { Warn "MACHINE $n not set (CI auto-discovery still applies; see scripts/ci_find_python.ps1)" } + else { Warn "MACHINE $n not set (CI auto-discovery still applies; -SetEnv writes it, docs/CI/ guide sections 4.4 / 4.5 explain the pairing)" } } exit 0 } @@ -466,6 +500,22 @@ if ($SetEnv) { if ($cpp) { Set-MachineEnv 'DTU_CPPCHECK' $cpp 'DTU_CPPCHECK' } else { Warn 'cppcheck not found (install Cppcheck.Cppcheck, or set DTU_CPPCHECK manually)' } + # ui-golden needs the SDL2 SDK and a checked-out tree never has it (see + # Find-Sdl2Sdk). Pin it machine-wide so the runner resolves it under SYSTEM too. + $sdl = Find-Sdl2Sdk + if ($sdl) { + Set-MachineEnv 'DTU_SDL2_DIR' $sdl 'DTU_SDL2_DIR' + $repoRoot = (Split-Path -Parent $PSScriptRoot) + [IO.Path]::DirectorySeparatorChar + if ($sdl.StartsWith($repoRoot, [StringComparison]::OrdinalIgnoreCase)) { + Warn 'DTU_SDL2_DIR points INSIDE this repo checkout -- a runner workspace will not contain that path.' + Warn 'copy an SDK to D:\DTU_SDK (or point DTU_SDL2_DIR at a machine-wide path), then re-run -SetEnv' + } + } + else { + Warn 'no SDL2 SDK found: the ui-golden gate fails with C1083 SDL2/SDL.h (measured run 45).' + Warn 'download it per the simulator deployment guide (docs/CI/ guide, section 4.5) or copy one to D:\DTU_SDK, then re-run -SetEnv' + } + $cl = Find-MsvcCl -ToolsetPrefix $MsvcToolset -Latest:$MsvcLatest if ($cl) { Set-MachineEnv 'DTU_MSVC_CL' $cl 'DTU_MSVC_CL' diff --git a/simulator/CMakeLists.txt b/simulator/CMakeLists.txt index 97f7449f..a3554a28 100644 --- a/simulator/CMakeLists.txt +++ b/simulator/CMakeLists.txt @@ -9,7 +9,15 @@ set(LVGL_DIR "${PROJECT_ROOT}/Lib/lvgl") set(UI_DIR "${PROJECT_ROOT}/Drivers/BSP/UI") set(NET_DIR "${PROJECT_ROOT}/Drivers/BSP/NET") set(NETCFG_DIR "${PROJECT_ROOT}/Drivers/BSP/NETCFG") -set(SDL2_DIR "${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10") +# SDL2 是**下载来的第三方 SDK**,simulator/SDL2/ 被 .gitignore 排除、**不入库**(§3.1 部署文档)。 +# 于是 CI/runner 的工作区里天然没有它:2026-10-27 远程 CI(run 45)第一次跑到 ui-golden 就 +# 报 C1083 找不到 SDL2/SDL.h——本地有 SDK 所以本地一直是绿的,照不出来。允许用机器级 +# DTU_SDL2_DIR 指向任意一份 SDK;**不设该变量时行为与今天完全一致**。 +if(DEFINED ENV{DTU_SDL2_DIR} AND NOT "$ENV{DTU_SDL2_DIR}" STREQUAL "") + set(SDL2_DIR "$ENV{DTU_SDL2_DIR}") +else() + set(SDL2_DIR "${CMAKE_CURRENT_SOURCE_DIR}/SDL2/SDL2-2.32.10") +endif() #============================================================================= # 编译标准 @@ -33,7 +41,21 @@ endif() # LVGL SDL 驱动内部 #include "SDL2/SDL.h",include 目录下已有 SDL2/ 子目录 set(SDL2_INCLUDE_DIR "${SDL2_DIR}/include" CACHE PATH "SDL2 include directory for LVGL") -#============================================================================= +# 缺 SDK 就在**配置期**一行说清。过去这里只打印拼出来的路径、不校验存在,于是配置阶段 +# 照样输出 "SDL2: <路径>",直到编译 LVGL 的 SDL 驱动才炸出几百行 C1083,看着像代码错了 +# 缺 SDK 就在**配置期**一行说清。过去这里只打印拼出来的路径、不校验存在,于是配置阶段 +# 照样输出 "SDL2: <路径>",直到编译 LVGL 的 SDL 驱动才炸出几百行 C1083,看着像代码错了 +# (CI run 45 的第一反应就是"像素回归红了吗")。查的是 SDL2_INCLUDE_DIR 而非 SDL2_DIR: +# 它是 CACHE 变量,复用旧 build 目录时缓存值可能比 SDL2_DIR 更旧,两处都得被这一条盖住。 +if(NOT EXISTS "${SDL2_INCLUDE_DIR}/SDL2/SDL.h") + message(FATAL_ERROR + "SDL2 SDK 不可用:找不到 ${SDL2_INCLUDE_DIR}/SDL2/SDL.h" + " - 本机首次部署:照 docs/UI设计/LVGL模拟器部署与使用说明.md §3.1 下载 SDK 到 simulator/SDL2/" + " - CI / runner:工作区里没有、也不该有这份 SDK;请设机器级 DTU_SDL2_DIR 指向一份 SDK" + " (scripts/ci_runner_bootstrap.ps1 -SetEnv 会自动发现并写入该变量)" + " - 若刚换过 SDK 位置:build 目录里的旧缓存可能仍指向旧路径,删掉 simulator/build 再配置" + ) +endif() # LVGL 子目录(引用项目自带的 LVGL v9.3.0 源码) #============================================================================= # LVGL 内部会自动检测 lv_conf.h(通过 LV_CONF_INCLUDE_SIMPLE 宏) diff --git a/simulator/build_sim.ps1 b/simulator/build_sim.ps1 index 606b6b77..511fef9b 100644 --- a/simulator/build_sim.ps1 +++ b/simulator/build_sim.ps1 @@ -117,6 +117,16 @@ if (Test-Path $cache) { } } +# 半截缓存检查:上一次配置如果在 CMakeLists 里 FATAL_ERROR 中断(例如缺 SDL2 SDK), +# CMakeCache.txt 已经写下、工程文件却根本没生成,于是下面「有缓存就不配置」会跳过配置, +# 失败点跑到离根因很远的 MSBuild(MSB1009: 项目文件不存在)——2026-10-27 在 CI 等价工作区 +# 实测:设好 DTU_SDL2_DIR 之后仍然红,红因就是这个残留缓存。与上面的生成器不符同理, +# 删掉 build/ 重来,让报错回到配置期那一行。 +if ((Test-Path $cache) -and -not (Test-Path (Join-Path $buildDir 'ALL_BUILD.vcxproj'))) { + Write-Host '[build_sim] build/ 有缓存但没有工程文件(上次配置中断)-> 删除重建' -ForegroundColor Yellow + if (-not (Remove-BuildDir $buildDir)) { exit 3 } +} + # --- 4. 配置 + 构建 --------------------------------------------------------- if (-not (Test-Path (Join-Path $buildDir 'CMakeCache.txt'))) { Write-Host '[build_sim] CMake 配置 ...'