ae80bb8e34
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,走自动发现)。
98 lines
10 KiB
Markdown
98 lines
10 KiB
Markdown
# 远程 CI(Gitea Actions)—— 本目录是唯一入口
|
||
|
||
> **适用范围(先读这句,可以省下很多时间)**
|
||
> 本仓库的远程 CI **只跑在本机这一台 runner 上**:runner 名 `win-stm32-ci`,装在
|
||
> `D:\Docker\act_runner`,连的是本机原生 Gitea `http://127.0.0.1:3001`。
|
||
> **其他机器、其他项目都不涉及,也不需要配这套东西。**
|
||
>
|
||
> 日常改代码**不需要读本目录**:提交前的等价自查是本地门禁,runner 在不在线都照样跑——
|
||
>
|
||
> ```powershell
|
||
> & '.venv\Scripts\python.exe' scripts/ci_local.py # L0 静态 + L0b 提交信息 + L0.5 主机单测
|
||
> ```
|
||
>
|
||
> 只有在这三种情况下才需要进来:**① 换机/重装后要重新部署 runner;② CI 不出任务或莫名
|
||
> 取消;③ 改 `.gitea/workflows/ci.yml` 本身。**
|
||
|
||
## 索引
|
||
|
||
全部内容是**一篇**文档:[`远程CI完整指南.md`](远程CI完整指南.md)(2026-10-12 由原「环境与部署」
|
||
目录下的两篇指南 `CI配置指南.md` 与 `Gitea_Runner部署指南.md` 合并而来,**两篇均已删除**,
|
||
去重后为单一来源)。
|
||
|
||
> **agent 排障时的操作顺序**在 **`remote-ci-ops` skill**(`.dsh/skills/remote-ci-ops/SKILL.md`,
|
||
> 用到自动加载):症状 → 一条判据 → 动作,加上改 workflow / 门禁脚本的红线与改后验收清单。
|
||
> 分工是——**本目录讲"为什么"和"怎么装",skill 讲"出问题时按什么顺序查"**,同一事实不在两处详述。
|
||
|
||
| 想干什么 | 去看 |
|
||
|---|---|
|
||
| 先搞清 CI 需要哪三样东西就位、本机拓扑、部署前置清单 | §0 / §0.1 / §0.2 |
|
||
| **CI 体积门禁现在怎么守**(同码凭证强制,「只声明不执行」那处盲区已关闭;gitlint 空转仍在) | §0.0 + §7 排障表"钩子条数对不上"行 |
|
||
| 服务端开启 Actions、`DEFAULT_ACTIONS_URL` 到底管什么 | §1(含 §1.2.1,**这条最容易搞反**) |
|
||
| 下载 / 注册 runner(`--instance` 用回环、labels 必须精确匹配 `runs-on`) | §2.2 |
|
||
| `config.yaml` 各字段语义(capacity / timeout / labels / 日志目录…) | §2.3 |
|
||
| **常驻:为什么必须看门狗、不能只起 daemon、也不能做成 Windows 服务** | §2.4 + §3.0 |
|
||
| runner 装什么软件、为什么变量必须对 **SYSTEM** 可见、机器级 `DTU_*` | §4(§4.1.1~§4.1.3 是三个真坑) |
|
||
| **覆盖率工具链(mscoverage)怎么装、机器级 MSVC 怎么指**(含三条可直接粘贴的管理员命令) | §4.4 |
|
||
| workflow 位置 / 触发 / 内网拉不到 `actions/checkout` 的三种处置 | §5(§5.3 零依赖写法) |
|
||
| 两个 job 各自跑哪些步骤、两条 Python 探测链要同序同步 | §5.5 |
|
||
| 端到端验证清单、无网页登录时直查 MySQL 核验 | §6 / §6.1 |
|
||
| 出问题了按症状查表(含 `0x41301`、1053、72h 被杀、`.ps1` 无 BOM 乱码…) | §7 |
|
||
| 日常维护(改钩子集 / 改 workflow 后要不要动 runner) | §8 |
|
||
| 一键自检脚本、最小操作清单 TL;DR | §9 / §10 |
|
||
|
||
## 常用命令速查
|
||
|
||
```powershell
|
||
# 本地等价门禁(不依赖 runner,日常用这个)
|
||
& '.venv\Scripts\python.exe' scripts/ci_local.py
|
||
|
||
# runner 体检(四项:计划任务 / 进程 / Gitea 可达 / 机器级变量)—— 须管理员
|
||
& '.\scripts\ci_runner_bootstrap.ps1' -Status
|
||
|
||
# 干跑:只打印将要写入的启动器与任务对象,不落盘不注册(免管理员)
|
||
& '.\scripts\ci_runner_bootstrap.ps1' -DryRun
|
||
|
||
# 部署 / 修复:写机器级 DTU_* + 生成看门狗启动器 + 注册开机计划任务 + 立即启动(须管理员)
|
||
& '.\scripts\ci_runner_bootstrap.ps1' -SetEnv -StartNow
|
||
```
|
||
|
||
> ⚠️ 上面三条 `ci_runner_bootstrap.ps1` 是**本机运维**动作,会改系统计划任务与机器级环境
|
||
> 变量;返回码 `0x41301` 是"任务正在运行"、`next run` 为空是开机触发器的正常表现,
|
||
> 都不是故障(详见指南 §3.4)。
|
||
|
||
> ✅ **机器侧遗留已清理(2026-10-12)**:旧方案的 `deploy_runner_task.ps1` 与 `run_runner.cmd`
|
||
> 已从 `D:\Docker\act_runner\` **删除**(它们跑单次 daemon,首次 Declare 失败就永久停摆,
|
||
> 机理见指南 §3.2;`check_env.ps1` 里指向它们的提示也已改指 bootstrap)。
|
||
> 现行部署只有 `ci_runner_bootstrap.ps1` 这一条路。
|
||
|
||
> 🔴 **重跑部署前先知道这条**(2026-10-12 实测):`Unregister-ScheduledTask` **不会**带走旧实例
|
||
> 启动的进程。旧看门狗会作为孤儿继续养着它的 daemon,而那个 daemon 占着 `.runner.lock`,
|
||
> 于是新看门狗每 10 秒失败一次(`runner file is already locked by another process`)——
|
||
> **而 runner 在网页上照样显示 online**,CI 看着正常,任务其实谁也没管。
|
||
> 现在 `-StartNow` 会先清掉这些残留进程再启动(约离线几秒),`-Status` 也会报出重复实例。
|
||
> **2026-10-12 已实测收敛**:清掉 3 个残留后 `daemon up: pid … (single instance)`,
|
||
> 监管链 `svchost(任务) → 看门狗 → daemon` 恢复,runner 回 online。
|
||
|
||
## 相关文档
|
||
|
||
- 测试分层与门禁决策(L0~L3 各层由谁把关):[`../测试说明/测试开发标准流程.md`](../测试说明/测试开发标准流程.md)
|
||
- workflow 本体:[`../../.gitea/workflows/ci.yml`](../../.gitea/workflows/ci.yml)
|
||
- 本地等价脚本:[`../../scripts/ci_local.py`](../../scripts/ci_local.py)
|
||
- runner 机器的工具链环境要求:[`../环境与部署/项目部署环境要求.md`](../环境与部署/项目部署环境要求.md)
|
||
- `DTU_*` 工具路径变量机制:[`../环境与部署/本地工具路径环境变量方案.md`](../环境与部署/本地工具路径环境变量方案.md)
|
||
|
||
## 变更记录
|
||
|
||
| 日期 | 变更 |
|
||
|---|---|
|
||
| 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 |
|
||
| 2026-10-12 | 🔀 本轮 CI 修复随 `master` **合入 `v2-on-ui`**(该分支另有 21 个功能提交;冲突仅 `AGENTS.md` 与 `docs/README.md` 两处,解法是"保留其新版结构 + 把 CI 指针搬进对应位置")。因该分支门禁更多(**26 钩子 / 10 单测套件** vs master 的 18 / 3),指南里写死的数字已改为**规则 + 分分支快照**,见指南 §6 / §7 / §10 |
|
||
| 2026-10-12 | ✅ 双看门狗缺陷**实测收敛**(管理员 `-StartNow` 清掉 3 个残留 → 单实例、链路恢复、runner online)。另修两处:daemon 日志走 stderr 被 PS 5.1 `2>&1` 包成 ErrorRecord(2 行真日志写成 8 行,每 4 行 3 行噪声)→ 启动器改 `ForEach-Object { "$_" }`;`-Status` 诚实化(非管理员读不到 SYSTEM 进程命令行 → 明说需提权而非谎报"无看门狗";重启循环判据改看进程存活时长,不再靠不轮转日志的尾部字符串报假警)。详见指南 §7.3 |
|
||
| 2026-10-12 | 🔴 **修复"双看门狗抢 `.runner.lock`"缺陷**:`Unregister-ScheduledTask` 不停旧实例进程 → 孤儿看门狗养着旧 daemon 占锁,新看门狗每 10 秒失败一次,而 runner 网页照样 online、`-Status` 也说"在跑"。现 `-StartNow` 先清残留进程再启动、`-Status` 报重复实例与锁冲突。同日清理机器侧:`config.yaml` 头部错误指引("改用 sc.exe / New-Service")改为看门狗口径,删除已废弃的 `deploy_runner_task.ps1` 与 `run_runner.cmd`,`check_env.ps1` 提示同步改指 bootstrap |
|
||
| 2026-10-12 | **单一口径清理**:合并稿初版对 4 处分歧"两说并存",现统一为本机实测现状——注册口径(`gitea-runner v4.0.1` / `D:\Docker\act_runner` / `127.0.0.1:3001`,旧 `act_runner v0.2.11` + `C:\act_runner` 段删去可复制命令只留警示)、`config.yaml`(`fetch_timeout: 1m0s`、`timeout: 30m0s`)、常驻方式(只认看门狗,`deploy_runner_task.ps1` 标废弃)、验收数字(L0.5 `288/288`,历史数字标注为快照)。并补"机器侧遗留脚本勿误用"警示 |
|
||
| 2026-10-12 | **新建本目录**:远程 CI 内容原先散在 `docs/环境与部署/`(两篇互相重叠的指南)、`docs/测试说明/测试开发标准流程.md` §9、`AGENTS.md` 提交门禁节共 4 处,约 2000 行;现合并为单一指南并把 `AGENTS.md` 压成一行指针。动因:远程 CI 只涉及本机一台 runner,与日常开发规范混在一起每次会话都被读,造成困惑 |
|