diff --git a/docs/CI/README.md b/docs/CI/README.md index 3530e8a8..91e56769 100644 --- a/docs/CI/README.md +++ b/docs/CI/README.md @@ -56,6 +56,11 @@ > 变量;返回码 `0x41301` 是"任务正在运行"、`next run` 为空是开机触发器的正常表现, > 都不是故障(详见指南 §3.4)。 +> 🗑 **机器侧遗留,别误用**:`D:\Docker\act_runner\` 下还留着旧方案的 `deploy_runner_task.ps1` +> 与 `run_runner.cmd`。它们跑的是**单次** daemon——首次 Declare 失败就永久停摆(机理见指南 +> §3.2),而且**执行它会把看门狗任务覆盖回单次任务**。现行部署只有 `ci_runner_bootstrap.ps1` +> 这一条路;那两个文件建议改名加 `.deprecated` 后缀或直接删除。 + ## 相关文档 - 测试分层与门禁决策(L0~L3 各层由谁把关):[`../测试说明/测试开发标准流程.md`](../测试说明/测试开发标准流程.md) @@ -68,4 +73,5 @@ | 日期 | 变更 | |---|---| +| 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,与日常开发规范混在一起每次会话都被读,造成困惑 | diff --git a/docs/CI/远程CI完整指南.md b/docs/CI/远程CI完整指南.md index 6e93d73d..9bd2027e 100644 --- a/docs/CI/远程CI完整指南.md +++ b/docs/CI/远程CI完整指南.md @@ -212,26 +212,18 @@ CI 的两个 job 都是 `runs-on: windows`,所以**必须有一个 labels 含 > 💡 **Token 存在于 `action_runner_token` 表**(MySQL,见本文 §6.1)。 > 每仓库一个有效 token,**注册一次后即被消费**;过期(约 1 小时)或已用则需重新创建。 -### 2.2 下载并注册 act_runner +### 2.2 下载并注册 runner -> ⚠️ 下面这段是初版(act_runner v0.2.x 时代)的通用写法,按原样保留备查; -> 本机实际部署请按 §2.2.1 起(**gitea-runner v4.0.1**,二进制名与目录都不同)。 +**本机现状(唯一口径)**:二进制 **`gitea-runner.exe` v4.0.1**(实测 20,623,872 B ≈ 20.6 MB), +装在 **`D:\Docker\act_runner`**,注册 `--instance` 用 **`http://127.0.0.1:3001`**。 +具体步骤见 §2.2.1 起,命令集与字段语义见 §2.2.2 / §2.3。 -```powershell -# 1) 下载(示例版本,请取 releases 页最新 Windows amd64) -# https://gitea.com/gitea/act_runner/releases -Invoke-WebRequest -Uri "https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-windows-amd64.exe" ` - -OutFile "C:\act_runner\act_runner.exe" - -# 2) 注册:URL 用 Gitea 根地址,token 用 2.1 复制的 -cd C:\act_runner -.\act_runner.exe register ` - --no-interactive ` - --instance "https://gitea.bitnasdaq.vip" ` - --token "粘贴 Registration Token" ` - --name "win-stm32-ci" ` - --labels "windows:host" -``` +> 🗑 **历史备查,勿照抄**:本文初版写的是 `act_runner` **v0.2.11** + `C:\act_runner` + +> `--instance https://gitea.bitnasdaq.vip`。三点均已过时,且**本机从未这样部署过** +> (2026-10-12 核实:`C:\act_runner` 至今不存在): +> ① v0.2.x 与 v4 的**二进制名与命令集都不同**(v4 已无 `daemon install/start/stop/status`,见 §3.0); +> ② 目录不同;③ `--instance` 填公网域名会多绕一圈 DNS + 反代,反代没配对内网 IP 就直接失败。 +> **照抄那段会装出一个连不上 Gitea 的 runner**,故原命令块已删除,只留此警示。 **关键点**:`--labels "windows:host"` 中的 `windows` 必须**逐字**与 workflow 的 `runs-on: windows` 对应(Gitea runner 的 label 名是自定义的,默认是 `ubuntu-latest`, @@ -325,14 +317,15 @@ cd "D:\Docker\act_runner" ### 2.3 配置 `config.yaml`(可选但建议) -`C:\act_runner\config.yaml` 里确认/补充: +`D:\Docker\act_runner\config.yaml` 里确认/补充(本机完整落地版见 §2.3.2): ```yaml runner: labels: - "windows:host" # 与 register 一致 capacity: 1 # 并行 job 数;单机建议 1,避免 cppcheck 抢 CPU - timeout: 30m + timeout: 30m0s + fetch_timeout: 1m0s # 🔴 不得超过 RPC timeout(1m0s),否则被自动截断并告警 cache: enabled: true ``` @@ -370,7 +363,7 @@ runner: file: .runner # 注册凭据(由 register 生成,勿手改) capacity: 1 # 并行 job 数;单机建议 1(cppcheck 吃 CPU) timeout: 30m0s # 单 job 超时;L0+L0.5 约 1~3 分钟 - fetch_timeout: 5m0s + fetch_timeout: 1m0s # 🔴 不得超过 RPC timeout(1m0s):原写 5m0s 会被自动截断并刷告警,2026-10-02 已改 fetch_interval: 2s # 🔴 标签:windows 必须与 workflow 的 runs-on: windows 逐字一致 labels: @@ -473,10 +466,11 @@ powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -Status ## 3. 常驻底层:为什么不能用 Windows 服务 + 任务计划程序手动方式 -> 本节是 2026-10-02 的实测记录("不能用服务"的根因 + 任务计划程序手动方式), -> 保留作根因依据与手动排障参考。**正式部署一律用 §2.4 的 `scripts/ci_runner_bootstrap.ps1`**—— -> 本节 §3.2 / §3.3 生成的 `run_runner.cmd` 跑的是**单次** daemon,2026-10-11 实测证明 -> 它首次 Declare 失败就退出、死了没人管(见 §2.4)。 +> 本节是 2026-10-02 的实测记录("不能用 Windows 服务"的根因)与任务对象字段说明, +> 保留作根因依据与手动排障参考。**正式部署一律用 §2.4 的 `scripts/ci_runner_bootstrap.ps1`**: +> §3.2 的旧脚本 `deploy_runner_task.ps1` 生成的是**单次** daemon,2026-10-11 实测证明它 +> 首次 Declare 失败就退出、死了没人管;§3.3 已改写为与现行 bootstrap **等价**的看门狗任务 +> 对象,只用于理解字段或在别的机器上手搓。 ### 3.0 🔴 关键结论:**不能用 Windows 服务** @@ -529,57 +523,62 @@ level=info msg="runner: win-stm32-ci, with version: v4.0.1, with labels: [window **让这个窗口开着**,去 Gitea 页面确认 runner 是 Idle,然后 Ctrl+C 停掉、再做常驻。 -### 3.2 一键部署脚本 `deploy_runner_task.ps1`(2026-10-02 方案,已被 §2.4 取代) +### 3.2 🗑 已废弃:`deploy_runner_task.ps1`(2026-10-02 方案,被 §2.4 推翻) -管理员 PowerShell 执行: +**不要再用它部署。** 它生成的 `run_runner.cmd` 跑的是**单次** `gitea-runner.exe daemon` +(无循环、无重启语义),而 §2.4 已实测证明 daemon 首次 Declare 失败就**正常退出**—— +计划任务把它记成"成功结束",`-RestartCount` 永不触发,于是 Gitea 重启一次 CI 就永久停摆。 + +脚本流程(供辨认用):提权检查 → 前置文件检查 → 清理旧 Windows 服务 → 生成 `run_runner.cmd` +→ `Register-ScheduledTask`(开机+登录双触发器、SYSTEM、最高权限、崩溃重启)→ 启动 → 校验存活。 +它的"崩溃重启"参数看着齐全,但**对"正常退出"完全无效**——这正是它失效的那一点。 + +> ⚠️ **这两个文件目前仍留在磁盘上**(`D:\Docker\act_runner\deploy_runner_task.ps1` 与 +> `run_runner.cmd`,2026-10-12 核实)。它们不在版本库里,属机器侧遗留;建议改名加 +> `.deprecated` 后缀或直接删除——**否则下次手滑执行,就会把看门狗任务覆盖回单次任务**, +> 等于把 §2.4 的修复整个抹掉。现行部署一律用仓库里的 `scripts/ci_runner_bootstrap.ps1`。 + +### 3.3 手动等价命令(等价于 §2.4 的 bootstrap,用于理解任务对象) + +日常请直接跑 `scripts/ci_runner_bootstrap.ps1 -SetEnv -StartNow`。下面是它注册的**等价**对象, +只在需要理解字段或在别的机器上手搓时使用: ```powershell -powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\deploy_runner_task.ps1" -``` +$Dir = 'D:\Docker\act_runner' +$launcher = Join-Path $Dir 'run_runner_watchdog.ps1' # 由 bootstrap 生成的看门狗 -脚本流程:提权检查 → 前置文件检查(`gitea-runner.exe` / `config.yaml` / `.runner`) -→ **清理旧的无效 Windows 服务**(若存在)→ 生成启动包装 `run_runner.cmd` -→ `Register-ScheduledTask`(开机+登录双触发器、SYSTEM 账户、最高权限、崩溃重启) -→ 启动任务 → 校验进程存活。 +# ① 动作:跑【看门狗】而不是 daemon 本体(单次 daemon 就是 §3.2 失效的根因) +$action = New-ScheduledTaskAction -Execute 'powershell.exe' ` + -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$launcher`"" ` + -WorkingDirectory $Dir -### 3.3 手动等价命令 +# ② 触发器:只要开机触发 +$trigger = New-ScheduledTaskTrigger -AtStartup +$trigger.Delay = 'PT1M' # 给 Gitea 启动留时间(看门狗自己也会等,双保险) -不想用脚本时,管理员 PowerShell 逐条执行: +# ③ 设置 +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries ` + -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) ` + -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew +$settings.StartWhenAvailable = $true # 🔴 属【任务设置】,写在触发器上会抛异常 -```powershell -$Dir = "D:\Docker\act_runner" +Register-ScheduledTask -TaskName 'GiteaActionsRunner' -Action $action -Trigger $trigger ` + -Settings $settings -User 'SYSTEM' -RunLevel Highest ` + -Description 'Gitea Actions Runner for STM32F4-Base CI (labels: windows:host)' -# ① 包装脚本(固定工作目录 + stdout 落盘) -@" -@echo off -cd /d "$Dir" -"$Dir\gitea-runner.exe" daemon --config "$Dir\config.yaml" >> "$Dir\log\runner-stdout.log" 2>&1 -"@ | Out-File "$Dir\run_runner.cmd" -Encoding ASCII - -# ② 注册任务 -$action = New-ScheduledTaskAction -Execute "$Dir\run_runner.cmd" -WorkingDirectory $Dir -$trgBoot = New-ScheduledTaskTrigger -AtStartup -$trgBoot.Delay = 'PT30S' -$trgLogon = New-ScheduledTaskTrigger -AtLogOn -$prin = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest -$sets = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries ` - -StartWhenAvailable -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1) ` - -ExecutionTimeLimit (New-TimeSpan -Seconds 0) -MultipleInstances IgnoreNew - -Register-ScheduledTask -TaskName "GiteaActionsRunner" -Action $action ` - -Trigger @($trgBoot, $trgLogon) -Principal $prin -Settings $sets ` - -Description "Gitea Actions Runner for STM32F4-Base CI (labels: windows:host)" - -# ③ 启动 -Start-ScheduledTask -TaskName "GiteaActionsRunner" +# ④ 启动 +Start-ScheduledTask -TaskName 'GiteaActionsRunner' ``` > ⚠️ **`ExecutionTimeLimit = 0`(不限时长)必须显式设置**—— > 任务计划程序默认 72 小时强制结束任务,会让 runner 每 3 天被杀一次。 > ⚠️ **`-WorkingDirectory` 只对 `New-ScheduledTaskAction` 有效**(这是它区别于 -> WinPS 5.1 `New-Service` 的地方)。不过仍建议用 `.cmd` 包装再 `cd /d`, -> 因为 SYSTEM 账户的默认工作目录不可控。 +> WinPS 5.1 `New-Service` 的地方)。SYSTEM 账户的默认工作目录不可控,所以启动器里 +> 仍要显式 `Set-Location` 或用绝对路径。 + +> 🗑 旧方案还额外挂了 `-AtLogOn` 触发器并以 `-LogonType ServiceAccount` 运行。现行口径 +> **只留开机触发**:runner 与登录会话无关,多一个触发器只会让人误以为"必须有人登录才行"。 ### 3.4 任务管理速查 @@ -605,9 +604,12 @@ Unregister-ScheduledTask -TaskName GiteaActionsRunner -Confirm:$false # 完全 > | `267014` (`0x41306`) | 任务被用户终止 | > > **判断 runner 是否健康,看这三项而不是 `LastTaskResult`**: -> ① `Get-Process gitea-runner` 有进程; -> ② `log\runner-stdout.log` 最新一行是 `declare successfully`; +> ① `Get-Process gitea-runner` 有进程(其父进程应是跑 `run_runner_watchdog.ps1` 的 powershell); +> ② `log\runner-daemon.log` 最新一行是 `declare successfully`; > ③ Gitea 页面 runner 显示 **Idle**(或 MySQL 查 `action_runner.last_online`)。 +> +> ⚠️ **`Get-ScheduledTaskInfo` 的 `Next Run Time` 为空是正常的**——触发器是"开机 +1 分钟", +> 没有下一个日历时间可报;常驻进程靠看门狗保活,不靠计划器反复拉起。别把空值当成"任务坏了"。 ### 3.5 ⚠️ 清理遗留的无效服务 @@ -1003,7 +1005,7 @@ github.com 会在 checkout 步骤卡住/失败。三种处置: | 5 | `git push` 一次(或页面 Run workflow) | Actions 页出现运行中任务 | | 6 | `l0-gates` 日志 | `using python (…)` + `runner environment check passed`;`::notice::L0: pre-commit hooks -- passed=.. failed=0 skipped=..`;无 `.map` 时见 `::warning::` 体积门禁未跑 | | 7 | `l0-gates` 的 **提交信息门禁** 步骤 | `::notice::gitlint: N commit message(s) checked, 0 failed`(**这一行以前根本不存在**,见 §7) | -| 8 | `l05-host-unit-tests` 日志 | `::notice::L0.5: 3 runner(s), 278/278 assertions passed, fail=0` | +| 8 | `l05-host-unit-tests` 日志 | `::notice::L0.5: 3 runner(s), 288/288 assertions passed, fail=0`(**断言总数随单测增减而变**,2026-10-12 实测 288;判定只看 **`fail=0` 且 A==B**,不要拿历史数字当基准) | | 9 | job 运行页的 **Summary** 区块 | 出现 "L0 static gates" / "Commit message gate (gitlint)" / "L0.5 host-side unit tests" 小标题与汇总行 | | 10 | 提交记录旁出现 ✅ | CI 全绿 | @@ -1071,7 +1073,7 @@ date +%s # 对比 last_online | `daemon install` 报 `accepts at most 0 arg(s)` | **v4 已移除该子命令,且无任何服务子命令** | **改用任务计划程序**(§3) | | 🔴 `Start-Service gitea-runner` 报 **无法启动服务**(错误 1053) | **`daemon` 不是服务型程序**,不向 SCM 回报 `SERVICE_RUNNING` | **删除该服务,改用任务计划程序**(§3.0 / §3.5) | | `Register-ScheduledTask` 报 **拒绝访问** | Shell 未提权(`IsAdmin=False`) | 右键 PowerShell → **以管理员身份运行**(§3.6) | -| 任务 `LastTaskResult` 非 0 | 启动包装脚本路径/权限问题 | 看 `log\runner-stdout.log`;确认 `.cmd` 存在且为 ASCII 编码 | +| 任务 `LastTaskResult` 非 0 | 启动器路径/权限问题,或任务被手工终止 | 看 `log\runner-watchdog.log` 与 `log\runner-daemon.log`;确认 `run_runner_watchdog.ps1` 存在且为纯 ASCII;`0x41301` 是"正在运行"不是错误 | | `l0-gates` 报 `Cannot find: pwsh in PATH` | `shell: pwsh` = PS7,本机只有 WinPS 5.1 | 改 `shell: powershell` | | 🔴 `doc-links` 钩子本地过、CI 挂 | 引用了 **gitignore 的运行时文件**(如 `.dsh/skills/serial-assistant/scripts/state/*.json`),本地存在、干净克隆里没有 | 该行加状态词「未创建/不存在」等(§4.1.2) | | 任务跑满 72 小时被杀 | 计划任务默认 `ExecutionTimeLimit` | 设为 `0`(不限时长),见 §3.3 警示 | @@ -1093,14 +1095,14 @@ date +%s # 对比 last_online ```powershell # 方法 A:PowerShell 重存为 UTF8BOM -$c = Get-Content "D:\Docker\act_runner\deploy_runner_task.ps1" -Raw -Encoding UTF8 -[System.IO.File]::WriteAllText("D:\Docker\act_runner\deploy_runner_task.ps1", $c, (New-Object System.Text.UTF8Encoding($true))) +$c = Get-Content "D:\Docker\act_runner\check_env.ps1" -Raw -Encoding UTF8 +[System.IO.File]::WriteAllText("D:\Docker\act_runner\check_env.ps1", $c, (New-Object System.Text.UTF8Encoding($true))) ``` ```bash # 方法 B:直接前缀 BOM 字节(bash/Git Bash) cd "/d/Docker/act_runner" -for f in deploy_runner_task.ps1 check_env.ps1; do +for f in check_env.ps1; do printf '\xef\xbb\xbf' > "$f.tmp" && cat "$f" >> "$f.tmp" && mv "$f.tmp" "$f" done ``` @@ -1108,7 +1110,7 @@ done **校验**:用 PowerShell 解析器(不执行)确认语法干净: ```powershell -foreach ($f in @("deploy_runner_task.ps1","check_env.ps1")) { +foreach ($f in @("check_env.ps1","run_runner_watchdog.ps1")) { $errs = $null $null = [System.Management.Automation.Language.Parser]::ParseFile( "D:\Docker\act_runner\$f", [ref]$null, [ref]$errs) @@ -1119,7 +1121,11 @@ foreach ($f in @("deploy_runner_task.ps1","check_env.ps1")) { > 💡 替代方案:改用 **PowerShell 7+(`pwsh`)**,它默认按 UTF-8 读脚本、无需 BOM。 > 但本机只有 WinPS 5.1,故加 BOM 最省事。 > -> 💡 **纯 ASCII 的脚本不受影响**。本目录的 `.ps1` 均含中文注释,现已统一加 BOM。 +> 💡 **纯 ASCII 的脚本不受影响,也不需要 BOM**。现行两个文件的口径: +> `check_env.ps1` 含中文注释 → **必须带 BOM**;`run_runner_watchdog.ps1`(由 +> `scripts/ci_runner_bootstrap.ps1` 生成)**刻意写成纯 ASCII**,因此对编码免疫—— +> 生成它的模板也要求纯 ASCII,理由见仓库 `AGENTS.md` 的编码铁律。 +> 🗑 旧脚本 `deploy_runner_task.ps1` 已废弃(§3.2),不再作为示例对象。 ### 7.2 日志位置与查看方式 @@ -1127,8 +1133,10 @@ foreach ($f in @("deploy_runner_task.ps1","check_env.ps1")) { | 日志 | 位置 | |---|---| -| runner **自身** stdout/stderr | `D:\Docker\act_runner\log\runner-stdout.log`(由 `run_runner.cmd` 重定向) | +| runner **自身** stdout/stderr | `D:\Docker\act_runner\log\runner-daemon.log`(由看门狗 `Out-File -Append -Encoding utf8` 落盘) | +| **看门狗自身**动作(等 Gitea / 拉起 / 退出码 / 重拉) | `D:\Docker\act_runner\log\runner-watchdog.log`(ASCII) | | **每个 job** 的详细日志 | `D:\Docker\act_runner\log\job\` | +| 🗑 历史遗留 | `log\runner-stdout.log` 是旧 `run_runner.cmd` 时代的文件,**已不再写入**;它内部混着 ASCII 与 UTF-16LE 两种编码(PS 5.1 `*>>` 默认 UTF-16LE),按 UTF-8 读会看到"每个字节后跟一个 NUL"的 `t i m e = ...` 形态,属正常现象不是新故障 | **看日志**:v4 的 `log` 段**没有 `file` 键**,两种落盘方式: @@ -1247,32 +1255,26 @@ Invoke-WebRequest -Uri "https://gitea.com/gitea/runner/releases/download/v4.0.1/ # 3) 前台试跑,确认能连上 .\gitea-runner.exe daemon # 看到 "labels: [windows] ... declare successfully" 即成功,Ctrl+C 停 -# 4) 常驻 —— ⚠️ daemon 不是服务型程序,必须用【任务计划程序】(管理员 PowerShell) -powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\deploy_runner_task.ps1" +# 4) 常驻 —— ⚠️ daemon 不是服务型程序,且首次 Declare 失败就直接退出进程 +# 所以必须【看门狗循环 + 开机计划任务】,一条命令搞定(管理员 PowerShell,在仓库根目录): +powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -SetEnv -StartNow -# 等价手动: -# Register-ScheduledTask -TaskName GiteaActionsRunner -Action ( -# New-ScheduledTaskAction -Execute "D:\Docker\act_runner\run_runner.cmd") ` -# -Trigger (New-ScheduledTaskTrigger -AtStartup) ` -# -Principal (New-ScheduledTaskPrincipal -UserId SYSTEM -LogonType ServiceAccount -RunLevel Highest) -# Start-ScheduledTask -TaskName GiteaActionsRunner +# 体检(四项:计划任务 / daemon 进程 / Gitea 可达 / 机器级 DTU_*)—— 须管理员: +powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -Status +# 干跑(只打印将要写入的启动器与任务对象,不落盘不注册)—— 免管理员: +powershell -ExecutionPolicy Bypass -File scripts\ci_runner_bootstrap.ps1 -DryRun -# 5) ⚠️⚠️ 把 pre-commit 装到【全局】site-packages(否则 L0 必挂,见 §4.1.1) -# —— 这是本环境最容易漏、也最难自己看出来的一步(用户态对 SYSTEM 不可见) -powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\install_precommit_global.ps1" -powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\install_precommit_global2.ps1" -# 若有包报"安装成功"但 import 失败(pip 静默跳过): -powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\fix_cfgv_global.ps1" - -# 5b) 2026-10-02 起推荐:改设机器级 DTU_* 变量(§4.1.3,显式覆盖、报错直指变量名); -# 全局 site-packages 安装保留为未设变量时的兜底。设完同样要重启 runner 任务。 -# (管理员 PowerShell;值按本机实际路径改) -# [Environment]::SetEnvironmentVariable('DTU_PRECOMMIT_PYTHON', 'C:\ProgramData\anaconda3\python.exe', 'Machine') +# 5) 工具链必须对 SYSTEM 可见(runner 以 SYSTEM 跑,看不见交互用户的 PATH 与用户级 site) +# 正式通道 = 机器级 DTU_* 变量,上一步的 -SetEnv 已自动发现并写入;需手工覆盖时: +# (管理员 PowerShell;下面是本机实测现值,换机按实际路径改) +# [Environment]::SetEnvironmentVariable('DTU_PRECOMMIT_PYTHON', 'C:\ProgramData\Anaconda3\python.exe', 'Machine') # [Environment]::SetEnvironmentVariable('DTU_CPPCHECK', 'C:\Program Files\Cppcheck\cppcheck.exe', 'Machine') # [Environment]::SetEnvironmentVariable('DTU_MSVC_CL', '<本机 x64 cl.exe 完整路径>', 'Machine') +# 兜底通道(只有变量没设时才会走到):把 pre-commit 装进【全局】site-packages,见 §4.1.1 +# ⚠️ 改过任何环境变量都必须 Stop 再 Start 任务——环境块是任务启动时的快照 # 6) 复验(-s = 模拟 SYSTEM 视角,必须过) -& 'C:\ProgramData\anaconda3\python.exe' -s -m pre_commit --version # pre-commit 4.6.2 +& 'C:\ProgramData\Anaconda3\python.exe' -s -m pre_commit --version # pre-commit 4.6.2 # 7) 回网页确认 runner 为 Idle → 触发 CI # push 触发(注意:纯 .md 改动在 workflow 的 paths-ignore 里,不会触发); @@ -1282,22 +1284,27 @@ powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\fix_cfgv_global.p # "https://gitea.bitnasdaq.vip/api/v1/repos/wandering1/STM32F4-Base/actions/workflows/ci.yml/dispatches" ``` -> ⚠️ 上面第 4 步是 2026-10-02 的方案(计划任务跑**单次** daemon)。2026-10-11 实测证明 -> 单次 daemon 首次 Declare 失败即退出、计划任务的失败重启救不到它—— -> **正式部署一律用 §2.4 的 `scripts/ci_runner_bootstrap.ps1`**(看门狗循环重拉 + 开机计划任务)。 +> 🗑 本清单第 4 步在 2026-10-02 版里是"计划任务跑**单次** daemon"(`deploy_runner_task.ps1`)。 +> 2026-10-11 实测证明单次 daemon 首次 Declare 失败即退出、计划任务的失败重启救不到它, +> 故已改写为看门狗方案;旧脚本的失效机理见 §3.2。 -> **CI 验收标准**(2026-10-02 已达成,run#8):`action_run_job` 里两个 job 均 `status=1`—— -> `l0-gates`(14 个 pre-commit 钩子全 Passed,`内存/Flash` 在无 `.map` 时 Skipped) -> + `l05-host-unit-tests`(MSVC 探测 + 39/39 单测)。 +> **CI 验收标准**:两个 job 都要 `status=1`。**现行口径**(2026-10-12):`l0-gates` 报 +> **17 条钩子**(`passed=16 skipped=1`,配置里定义 18 个,gitlint 属 `commit-msg` stage 故不在 +> `--all-files` 报告内,见 §7)+ 提交信息门禁 `::notice::gitlint: N checked, 0 failed`; +> `l05-host-unit-tests` 报 `3 runner(s), 288/288 assertions passed, fail=0`。 +> 🗑 历史快照(勿当基准):run#8(2026-10-02)当时是"14 个钩子全 Passed + 39/39 单测"—— +> 钩子数与断言数都随仓库演进而变,**判定只看 `fail=0`、`failed=0` 与 A==B**。 -**配套脚本**(均在 `D:\Docker\act_runner\`,**均已加 UTF-8 BOM**,可直接在 WinPS 5.1 运行): +**配套脚本**: -| 脚本 | 用途 | 需要管理员 | -|---|---|---| -| `check_env.ps1` | 环境自检(Gitea 可达 / 工具链 / MSVC / 任务状态) | 否 | -| `deploy_runner_task.ps1` | **一键部署**(自检 + 清旧服务 + 建计划任务 + 启动 + 验证) | **是** | +| 脚本 | 位置 | 用途 | 需要管理员 | +|---|---|---|---| +| `ci_runner_bootstrap.ps1` | **仓库 `scripts/`** | **现行唯一部署/体检入口**(`-Status` / `-DryRun` / `-SetEnv -StartNow`) | `-Status`、`-SetEnv -StartNow` 需要 | +| `check_env.ps1` | `D:\Docker\act_runner\` | 环境自检(Gitea 可达 / 工具链 / MSVC / 任务状态) | 否 | +| `run_runner_watchdog.ps1` | `D:\Docker\act_runner\` | **看门狗启动器**,由 bootstrap 生成,是计划任务的动作目标。**纯 ASCII,不需要 BOM** | 由任务以 SYSTEM 跑 | +| 🗑 `deploy_runner_task.ps1`、`run_runner.cmd` | `D:\Docker\act_runner\` | **已废弃**(单次 daemon,§3.2)。仍在磁盘上,建议改名加 `.deprecated` 或删除,免得误执行把看门狗任务覆盖回单次任务 | — | -> 早前基于 Windows 服务的 `deploy_runner.ps1` / `install_service.ps1` 已删除 +> 更早基于 Windows 服务的 `deploy_runner.ps1` / `install_service.ps1` 已删除 > —— 那条路走不通(§3.0)。 --- @@ -1306,6 +1313,7 @@ powershell -ExecutionPolicy Bypass -File "D:\Docker\act_runner\fix_cfgv_global.p | 日期 | 变更 | |---|---| +| 2026-10-12 | 🧹 **单一口径清理(以本机实测现状为准)**:合并稿初版对 4 处分歧采取"两说并存 + 衔接注",本次统一。① 注册口径——`act_runner v0.2.11` + `C:\act_runner` + 公网域名 instance 一段**删除可复制命令**,只留"勿照抄"警示(核实 `C:\act_runner` 至今不存在,本机是 `gitea-runner v4.0.1` + `D:\Docker\act_runner` + `127.0.0.1:3001`);② `config.yaml` 示例 `fetch_timeout` 由 `5m0s` 改为磁盘实际值 **`1m0s`**、`timeout` 统一 `30m0s`,并补"不得超过 RPC timeout(1m0s)";③ 常驻口径统一为看门狗——§3.2 `deploy_runner_task.ps1` 标为**已废弃**并说明失效机理(单次 daemon = 正常退出,失败重启永不触发),§3.3 手动等价命令**重写为看门狗版任务对象**(旧版会教人手搓出坏配置),§10 TL;DR 第 4 步改指 `ci_runner_bootstrap.ps1`;④ 数字口径——L0.5 期望值更新为 **288/288**(run#8 的 39/39 与 278/278 标注为历史快照,判定只看 `fail=0` 与 A==B),钩子数按现行 18 定义 / 17 报告。另同步:健康三查与排障表改用现行日志名(`runner-daemon.log` / `runner-watchdog.log`,`runner-stdout.log` 标为遗留且说明其混编码)、BOM 示例对象换成 `check_env.ps1` 并注明看门狗是纯 ASCII 无需 BOM、配套脚本表补 `run_runner_watchdog.ps1` 并标出磁盘上仍存的废弃脚本建议删除、补 `Next Run Time` 为空属正常的说明 | | 2026-10-12 | **文档合并**:本次把 `CI配置指南.md` 与 `Gitea_Runner部署指南.md` 合并为本篇(`docs/CI/远程CI完整指南.md`)并迁入 `docs/CI/`,去重(runner 注册 / config.yaml / 装软件 / 排障表两篇均有)、补入 runner 看门狗与机器级变量实测教训;相对链接按新位置改写,原两篇互引改为本文内部引用 | | 2026-10-11 | **bootstrap 自身修复**:`StartWhenAvailable` 误设到**触发器**上(该属性属**任务设置**,触发器没有)→ 脚本在"已注销旧任务、尚未注册新任务"之间抛 `SetValueInvocationException`,把 runner 任务删没了。现改为①先构建 action/trigger/settings、**后**注销旧任务,②`Register-ScheduledTask` 失败时显式告警"旧任务已删、CI 裸奔",③新增 **`-DryRun`**(免管理员即可全量校验任务对象),④`-Status` 在非管理员下额外提示"看不见任务 ≠ 任务不存在"。同时更正根因:旧任务其实存在,但跑的是**单次** `daemon`——daemon 属"正常退出"而非"失败",计划任务的失败重启**救不到**它,必须用看门狗循环重拉 | | 2026-10-11 | **修"很多任务不执行"(runner 静默停摆)+ 补 gitlint 门禁**:① §2.4 重写——`gitea-runner` v4 的 `daemon` **无服务支持、首次 Declare 失败即退出进程**(日志实证),Gitea/机器重启一次 CI 就停摆,**run 16~21 共 12 个 job 零日志**(`cancelled`/`queued`,叠加 `cancel-in-progress` 后表现为"任务凭空消失"而非失败);新增 **`scripts/ci_runner_bootstrap.ps1`**(等 Gitea 就绪 + 崩溃自拉起的看门狗 + 开机计划任务 + `-SetEnv` 写机器级 `DTU_*` + `-Status` 只读体检)。② §3.1 重写——SYSTEM 可见性三重坑(PATH python 是 msys64 无 pre-commit / 用户级 site 对 SYSTEM 不可见 / `.venv` 已 gitignore)是 run 14~15 L0 全灭根因(`33a4aa4` 把候选收窄成这两个、又删了 cppcheck 目录兜底,而机器级 `DTU_*` 从未设置);探测收进 **`scripts/ci_find_python.ps1`**(两 job 共用、可离线自测、失败列出全部候选)并恢复机器级安装兜底。③ 新增 **gitlint 提交信息门禁**:钩子定义数比 CI 报告条数多 **1**(当时 17/16,master 并入 `test-cppcheck` 后为 18/17),缺的正是 gitlint——其 `stages: [commit-msg]` 声明在**上游** `.pre-commit-hooks.yaml`(`--all-files` 跑不到,且表现为全绿 → 长期无人发现)→ **`scripts/ci_gitlint_range.ps1`** 逐条校验本次 push/PR 范围(消息文件须写在仓库内 + UTF-8 无 BOM)。④ `GITHUB_ENV/PATH/OUTPUT` 写入统一显式 `-Encoding ascii`(PS 5.1 `Out-File -Append` 默认 **UTF-16LE**,实测落盘 `42 00 42 00 42 00 3D 00 31 00`)。§5 清单 8→10 条、§6 排障表 +5 行、§7 维护项重写 |