fix(ci): run 块按点源语义加固——原生 stderr 变终止性错误曾致两 job 3 秒亡
run 44(本分支上一次推送)把上一版原生检出打回原形:L0 用了 3 s、L0.5 用了
2 s 双双失败,日志里是 error: No such remote 'origin' 被包成 NativeCommandError
后整步终止。根因在我上一版:runner 执行 run: 块的方式是 powershell -command
点源那个文件,会话继承的 $ErrorActionPreference 是 Stop,于是原生程序往 stderr
写的任意一行都会被 PS 5.1 包成终止性错误。我写的 git remote get-url origin
2>$null 正是这一类,而 2>$null 不算摊平,反而让它先把整步炸掉。
修法两件事一起做:块首显式 $ErrorActionPreference = Continue(不依赖继承);
所有 git 调用走同一个漏斗 Invoke-DtuGit,把 stderr 摊平成字符串再决定是否打印。
探针顺手换成 git config --get remote.origin.url:远端不存在时它只返回非零码、
不往 stderr 写,从源头不再触发这类错误。
更阴的一条教训是我的测法本身。第一版复现器用 -File 包一层点源,结果四条失败
路径全测成 rc=0。用最小矩阵把语义钉死后:exit 1 写在被点源的文件里时,-File
直跑 = 1;-File 包一层点源 = 0 且继续执行后续语句;-Command 点源(runner 的真
实用法)= 1。也就是说只要我一直用 -File 测,检出失败也会一路伪装成绿,这个洞
比上面那个 bug 更坏。现在统一用 runner 的真实语义测:powershell -NoProfile
-Command 点源块体文件,且前置 $ErrorActionPreference = Stop。
换测法后的实测(全部跑在从 ci.yml 抽出的那份字节上,不是另抄的副本):
- 点源 + Stop、全新工作区:ExitCode=0、checkout ok: c1e7e8c / commits=669、
dirty_files=0、150.9 s(修复前同一模式就是 run 44 的 3 秒亡);
- DTU_REPO_SHA 空 / DTU_REPO_URL 空 / sha 不在远端 / 坏令牌:ExitCode 均 1,
坏令牌 0.5 s 返回 unable to get password,不挂起、不烧 job 超时;
- 复用工作区:1.6 s,dirty_files=0,被忽略文件保留,未跟踪垃圾清除;
- 结构面:13 步、两 job 第 1 步逐字一致、uses: 计数 0、run 块非 ASCII 计数 0、
无 BOM 无 CR;check yaml Passed;check_doc_links --all 180 篇、无法判定 0。
文档同笔落地(教训要跟它救的那段代码一起进历史):指南 §5.3 补 run 44 实录与
退出码矩阵、实测表补 G/H 两行、§7 补 job 几秒就亡一行、两份变更记录各加一行;
remote-ci-ops skill §10 补 run: 块的正确测法。另记一个运维事实:推到已存在
的远端分支会正常建 run(run 44 即如此,推送后约 4 分钟入库,我先前把轮询窗口
取得太短误判成没建),以后验 workflow 不再需要手动 dispatch 与服务凭据。
下一轮可能红的两处我先标出来,红了正是我们要的证据:SYSTEM 下的 ui-golden
(历史上败在 32 位 MSBuild 与 VCTargetsPath)与 test-coverage(缺
DTU_MS_COVERAGE,走自动发现)。
This commit is contained in:
@@ -86,6 +86,7 @@
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-27 | run 44(推送到已存在分支会正常建 run)暴露的教训写进指南 §5.3:run: 块是被**点源**执行的,`$ErrorActionPreference` 继承 Stop 时原生 stderr 会终止整步;且测 run: 块只能用 `-Command ". 文件"`(`-File` 包点源会把失败测成 rc=0)。§7 补「几秒就亡」一行,skill §10 补测法 |
|
||||
| 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 |
|
||||
|
||||
@@ -1047,6 +1047,8 @@ set-url origin <server_url>/<repository>.git` → 完整 refspec `+refs/heads/*:
|
||||
| D | 灌一个坏令牌 | rc=1,`fatal: unable to get password from user`(**快速失败**,没挂住) |
|
||||
| E | 执行**从 `ci.yml` 抽出的**正文 | rc=0、与 A 同数;**零 ErrorRecord 噪声**;首次全量拉取 172 s(18.65 MiB / loopback) |
|
||||
| F | 同一工作区再跑一次 | rc=0、秒级——**稳态 CI 不重复付全量代价**,只有工作区全新时才慢 |
|
||||
| G | **点源 + `$ErrorActionPreference` 为 Stop(= runner 语义)**,全新工作区 | 修复后 rc=0、`checkout ok: c1e7e8c / commits=669`、首拉 168 s;**修复前同一模式 3 s 亡**(就是 run 44)——这一行才算真正的门 |
|
||||
| H | 点源模式下的四条失败路径 | `DTU_REPO_SHA` 空 / `DTU_REPO_URL` 空 / sha 不在远端 / 坏令牌 → **ExitCode 全 = 1**(成功与复用 = 0)。A–F 那批是用 `-File` 测的,G/H 起才换成语义正确的点源测法 |
|
||||
> ⚠ **一条未证的边界**:`pull_request` 也是本 workflow 的触发器,而 PR 事件的 `github.ref` 是
|
||||
> `refs/pull/<n>/merge`,**不在**我用的分支 refspec 内 → 会走「再显式 fetch 事件 ref」那条兜底。
|
||||
> 这台实例**至今 0 个 PR**(库里 `pull_request` 计数 = 0),`git ls-remote 'refs/pull/*'` 也返回空,
|
||||
@@ -1056,6 +1058,35 @@ set-url origin <server_url>/<repository>.git` → 完整 refspec `+refs/heads/*:
|
||||
> 不受影响)。同一条备注也适用于 `${{ github.server_url }}`:原生检出与 `checkout@v4` 用的是**同一个**
|
||||
> server_url 拼法,**可达性假设没有变化**——变的只是不再联网 github.com。
|
||||
|
||||
> 🔴 **run 44 真机首验栽了一次,两条教训都属于「本地照不出来」那一类**(10-27 10:38 推上
|
||||
> `ci/remote-ci` 之后两个 job 分别 **3 s / 2 s 亡**,日志原文如下):
|
||||
>
|
||||
> ```
|
||||
> git : error: No such remote 'origin'
|
||||
> + CategoryInfo : NotSpecified: (...) [], RemoteException
|
||||
> + FullyQualifiedErrorId : NativeCommandError
|
||||
> ##[error]Process completed with exit code 1.
|
||||
> ```
|
||||
>
|
||||
> ① **`2>$null` 不算摊平**。runner 把 `run:` 块**点源**进会话(`shell: powershell -command
|
||||
> . 那个文件`),会话的 `$ErrorActionPreference` 是 Stop 时,原生程序**任何写到 stderr 的一行**
|
||||
> 都会被 PS 5.1 包成**终止性** `NativeCommandError`——`2>$null` 不但没吞掉它,反而让它先把整
|
||||
> 个步骤炸掉(`git remote get-url` 在远端不存在时正是往 stderr 写)。修法两条一起做:块首
|
||||
> **显式** `$ErrorActionPreference = Continue`;所有原生 git 调用统一走一个把 stderr 摊平成
|
||||
> 字符串的漏斗(现在这份就是这么写的)。
|
||||
> ② **测法必须复现「点源」,否则连失败都测不出来**。把 `exit 1` 写在被点源的文件里时,
|
||||
> 三种调用方式的实测语义如下:
|
||||
>
|
||||
> | 调用方式 | `exit 1` 之后 | 进程退出码 |
|
||||
> |---|---|---|
|
||||
> | `powershell -File a.ps1` | 进程结束 | 1 |
|
||||
> | `powershell -File b.ps1`,而 b 里点源 a | **继续执行 b 的后续语句** | **0** |
|
||||
> | `powershell -Command ". a.ps1"` ← **runner 的用法** | 进程结束 | **1** |
|
||||
>
|
||||
> 我第一版复现器恰好用了中间那种,于是把四条失败路径全测成 `rc=0`(**假警报**,同时也
|
||||
> 照不出教训 ①)。**要验 workflow 里的 `run:` 块,只能用 `-Command` 点源**,并且最好显式
|
||||
> 把 `$ErrorActionPreference` 前置成 Stop——那才是 runner 的真实语义。
|
||||
|
||||
**自查(改 workflow 前后都跑,第二条是内联换来的同步义务)**:
|
||||
|
||||
```powershell
|
||||
@@ -1175,6 +1206,7 @@ date +%s # 对比 last_online
|
||||
| 任务一直 **Waiting / 排队** | 没有 label 匹配 `windows` 的 runner | 检查 `--labels "windows:host"`;Settings→Runners 是否 Idle |
|
||||
| `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 实录 |
|
||||
| `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<Y | 有单测用例未通过 | 看该模块的 `::error::unit test failed` 行与其 `ci-artifacts/l05-<模块>.log` |
|
||||
@@ -1467,6 +1499,7 @@ powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -DryRun
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 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 超时,**两者不是一回事**,别拿一个解释另一个 |
|
||||
| 2026-10-25 | 🔒 **A-5 残余销项联动(§0.0 重写)**:体积门禁由"CI 不守"改为**同码凭证强制**——本地 `@build` 0/0 后 `ram_baseline.py --update` 盖 `scripts/size_attestation.json`,L0 作业末尾 `check_size_gate.py --ci` 复核同码性 + 红线复算(runner 无需 Keil);主干 push 拦红、其余事件 warn-only;`mem-budget` 补进 SKIP 名单诚实显示。指纹取 git 索引 blob(跨 checkout 稳定,修 7c4628b);三项仍守不住的口径唯一正文 = 脚本 docstring |
|
||||
|
||||
Reference in New Issue
Block a user