chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
This commit is contained in:
@@ -59,7 +59,7 @@ PY=".venv/Scripts/python.exe"; TW=".dsh/skills/task-worktree/scripts/task_worktr
|
||||
|
||||
```bash
|
||||
"$PY" "$TW" new N21 dhara-isfree --needs-bench # 把新会话工作目录切到打印出来的 _wt/N21-dhara-isfree/
|
||||
"$PY" "$TW" lock acquire bench --task N21 # 烧录 / @test / 板级套件
|
||||
"$PY" "$TW" lock acquire bench --task N21 # 烧录 / /serial-boot-test / 板级套件
|
||||
"$PY" "$TW" lock release bench --task N21 --bench-pass
|
||||
"$PY" "$TW" done N21 # 回主工作树(铁律 1);先 --dry-run 读全量
|
||||
```
|
||||
@@ -110,7 +110,7 @@ git add <上面刷出来的基线/快照文件> # 显式列文件,别 -A
|
||||
git commit -F <UTF-8 无 BOM 的正文文件> # 提交属 Ask first(AGENTS.md 边界)
|
||||
```
|
||||
|
||||
为什么必然要补 → 陷阱「`done` 的门禁跑在 `@build` 之前」。合并解过冲突时,**合并提交期 pre-commit 只查冲突文件**(→ G 节第 3 条)→ 落主干后必须另跑全量。
|
||||
为什么必然要补 → 陷阱「`done` 的门禁跑在 `/keil-build` 之前」。合并解过冲突时,**合并提交期 pre-commit 只查冲突文件**(→ G 节第 3 条)→ 落主干后必须另跑全量。
|
||||
**注释/文档-only 但碰了 .c/.h 的短链**:先比 `Program Size` 与合并前是否逐项相同——相同就只 `ram_baseline.py --update` 重写体积凭证(`scripts/size_attestation.json` 跟着补提交笔走),**别 `--write` 动受管区**(等于给在飞分支造冲突面);数字变了才走上面全链。**已命令化**:`"$PY" "$TW" postmerge`——新鲜直接跳过;红则先短链,再用只读 `check_mem_numbers` 复检裁定是否升全链;末尾跑 `--all` 并列出待提交文件(不代提交)。上面的手工全链仍是权威兜底。
|
||||
6. **重跑 `done` 前主树必须干净**,否则 `失败:主工作树不干净(…)——合并会把未提交改动卷进来` → 第 5 步产物先提交。分支已并时重跑打印 `[i] task/<ID>-<slug> 已合入 <base>,跳过合并,直接清场`——**该路径在门禁之前就返回,不会替你复绿**:上一次门禁没跑到的尾巴(主树 `ci_local.py` + `check_doc_links.py --all`;体积/文档那半可 `postmerge` 一把跑)必须自己补。主树脏且那份脏不是你的 → **C2**。
|
||||
7. 清场后再敲一次 `done <ID>` 得 `前置/环境错误:任务 <ID> 不存在(缺台账 …json)`(exit 2)——**不是失败**:清场最后一步 unlink 了台账。**判完成以 `git log` 为准**。
|
||||
@@ -153,7 +153,7 @@ git commit -F <UTF-8 无 BOM 的正文文件> # 提交属 Ask first(AGENTS.md
|
||||
|
||||
1. **`done` 只能在主工作树执行**——主干分支检出在主树,git 不允许同一分支两处检出。
|
||||
2. **`done` 不 push**:它结束于本地合入 + 门禁全绿;推送需用户明确许可。
|
||||
3. **`build` 与 `bench` 都必须串行**(UV4 不能两个实例同时 `-b`;COM6、ST-LINK、设备 IP `192.168.1.100` 各一份)。`build` 锁由 **@build 自持自释**(`keil_build.py`)→ 调用方**不要**手工 acquire/release;`bench` 锁**必须手工** acquire → 烧录 / `@test` → release `--bench-pass`。非登记持有者怎么放 → B 节;插件树 → F 节。
|
||||
3. **`build` 与 `bench` 都必须串行**(UV4 不能两个实例同时 `-b`;COM6、ST-LINK、设备 IP `192.168.1.100` 各一份)。`build` 锁由 **/keil-build 自持自释**(`keil_build.py`)→ 调用方**不要**手工 acquire/release;`bench` 锁**必须手工** acquire → 烧录 / `/serial-boot-test` → release `--bench-pass`。非登记持有者怎么放 → B 节;插件树 → F 节。
|
||||
4. **单写者文档**:`docs/任务规划/`(整个文件夹)、`AGENTS.md` 任务表块、`MDK-ARM/*.uvprojx`、`.pre-commit-config.yaml`、内存快照 —— 任务期**不在任务树里改**,留到 `done` 统一处理;用户显式豁免时(在任务树写、由 done 合回)要在交付报告里点名这条例外。
|
||||
5. **清场必须先摘联接**:脚本先 `os.rmdir` 掉 `.venv` / `simulator/SDL2` 联接再 `git worktree remove`。手工拆树前必跑 **E 第 3 步**预扫拿到 `REPARSE: none`——否则 git 可能递归进联接把主树 `.venv` 一起删掉。
|
||||
6. **活跃任务硬上限 8 = 脚本常量 `MAX_CONCURRENT`(`task_worktree.py:30`)——唯一事实源**:报错文案与 `new` 的插件树提示都插值它,AGENTS.md / 方案文档 / frontmatter / A 节的数字全是引用同步。改这个数字 → 全形态扫描走 `stale-doc-count-sweep` skill + 隔离小仓验证上限真生效;每棵磁盘代价按实测口径重述、别照抄旧数(细节 → 验证清单末条)。满了 exit 2 → A;悬挂台账 → E 第 6 步。
|
||||
@@ -163,11 +163,11 @@ git commit -F <UTF-8 无 BOM 的正文文件> # 提交属 Ask first(AGENTS.md
|
||||
|
||||
## 陷阱与验证(收工前各扫一遍)
|
||||
|
||||
- [reference/pitfalls.md](reference/pitfalls.md):全部实测坑逐条(含 `--dry-run` 不保护清场段、门禁跑在 `@build` 之前、未跟踪文件在 `git log` 与合并里都不留痕、并集合并残留、新树从未构建 → 体积类门禁必然红、注释-only 也刷红体积凭证、「已合入」快路径在门禁前返回不替你复绿、`--force` 只覆盖四类检查、联接不是 symlink(`core.symlinks=false`)、锁不绑进程存活、文件工具先读后改按绝对路径记账、中文门禁回显乱码但 rc 可信)。收工前扫一眼。
|
||||
- [reference/pitfalls.md](reference/pitfalls.md):全部实测坑逐条(含 `--dry-run` 不保护清场段、门禁跑在 `/keil-build` 之前、未跟踪文件在 `git log` 与合并里都不留痕、并集合并残留、新树从未构建 → 体积类门禁必然红、注释-only 也刷红体积凭证、「已合入」快路径在门禁前返回不替你复绿、`--force` 只覆盖四类检查、联接不是 symlink(`core.symlinks=false`)、锁不绑进程存活、文件工具先读后改按绝对路径记账、中文门禁回显乱码但 rc 可信)。收工前扫一眼。
|
||||
- [reference/verification.md](reference/verification.md):每节要留的证据(预演 sha + `MT_RC=0`、blob id 对账、两把锁空闲、禁词计数 0、`REPARSE: none`、改上限数字后的全形态扫描)。
|
||||
|
||||
## 与其它 skill 的衔接
|
||||
|
||||
- 构建 = `keil-build`(`@build`)· 烧录/回读 = `stlink-flash` · 串口闭环 = `serial-boot-test`(真实 hex 路径以 `.serialtest/config.json` 的 hex 键为准——实测 `MDK-ARM/Objects/STM32F407-Demo.hex` 不存在,拷错 = 烧旧镜像)
|
||||
- 构建 = `keil-build`· 烧录/回读 = `stlink-flash` · 串口闭环 = `serial-boot-test`(真实 hex 路径以 `.serialtest/config.json` 的 hex 键为准——实测 `MDK-ARM/Objects/STM32F407-Demo.hex` 不存在,拷错 = 烧旧镜像)
|
||||
- 新 `.c` 双端注册 = `new-source-file` · RAM/.map 台账 = `mem-budget` · UI 回归 = `ui-verify` · 计数同步 = `stale-doc-count-sweep` · 卡死的技能台账行 = `mnemon-skill-ledger-stuck-repair`
|
||||
- DSH worktree 插件(`/worktree` / `worktree_create`,仓内 `.dsh-worktrees/`)与本 skill 的分工、桥接命令、锁归属 → F 节 · 完整机制与目录分配 → `docs/环境与部署/并行任务工作树方案.md`
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
它按缺的那一样分别给后果,`adopt` 没补齐也只报「还不能算就绪」,不许说「现在可以跑」。
|
||||
- **adopt 只碰联接**:已联的跳过;`simulator/SDL2` 若已是实体目录只告警不动;`.dsh-worktrees/` 之外的路径
|
||||
一律 exit 2(实测 `plugin adopt ".."` 被拒)——这道闸防的是把主树或 `_wt` 目录当插件树遮蔽掉真实内容。
|
||||
- **锁归属现在认得出来**:`@build` 在插件树里自持自释时,持有者名从 cwd 反推成 `pw:<name>`(多段名压平成
|
||||
- **锁归属现在认得出来**:`/keil-build` 在插件树里自持自释时,持有者名从 cwd 反推成 `pw:<name>`(多段名压平成
|
||||
`-`),与 `plugin adopt` 打印的那个名字逐字相同(`keil_build.py: plugin_tree_owner()` ↔
|
||||
`task_worktree.py: plugin_lock_owner()`)→ `lock status` 不再一律落成 `build-cli`。
|
||||
- 上台架仍须手工占锁、用同一个名:`lock acquire bench --task pw:<name> --note <说明>` →
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
subprocess,含空格的路径(本项目 `DTU Code`)由 subprocess 自动加引号。
|
||||
但**读属性**用 ctypes 没问题:`GetFileAttributesW` + `FILE_ATTRIBUTE_REPARSE_POINT` 就是 E 第 3 步的预扫手段。
|
||||
- **联接建失败不中止 `new`**:只告警并打印手工补建命令,避免留下半成品任务空间;
|
||||
但该任务里跑不了 `.venv\Scripts\python.exe`,`@build` / `ci_local.py` 会找不到解释器。
|
||||
但该任务里跑不了 `.venv\Scripts\python.exe`,`/keil-build` / `ci_local.py` 会找不到解释器。
|
||||
- **`done` 前主工作树必须干净**:否则未提交改动会被卷进合并(补完基线再重跑 `done` 时最容易忘,见 C 第 6 步)。
|
||||
- **⚠ 任务树里 `check_doc_links.py --all` 的红多半是 `simulator/SDL2` 目录联接的假红**:全库扫描会吞下那份
|
||||
「仓库里的第二份仓库副本」(实测任务树 222 个文档 / 35 处问题全在联接下、RC=1,而主树同命令基线全绿)。
|
||||
@@ -39,11 +39,11 @@
|
||||
`.dsh/skills/serial-boot-test/scripts/run_boot_test.py`):**不许用 stash / 提交 / 删除去清场**(项目级 Never,
|
||||
你也无从判断它写没写完)→ 走 **C2**:取证(mtime + diff + `hash-object` 存进 ci-artifacts)→ `merge-tree` 预演
|
||||
→ 主树 `merge --no-ff -F` → 合并前后 blob id 与 `diff --stat` 对账。**「它看着像废弃的」不是丢弃判据**。
|
||||
- **⚠ `done` 的门禁跑在 `@build` 之前 → 依赖 `.map` 的钩子会误报**(2026-10-12 实测):
|
||||
`done` 的顺序是「合并 → 重生成 → **门禁(ci_local)** → `@build`」,而
|
||||
- **⚠ `done` 的门禁跑在 `/keil-build` 之前 → 依赖 `.map` 的钩子会误报**(2026-10-12 实测):
|
||||
`done` 的顺序是「合并 → 重生成 → **门禁(ci_local)** → `/keil-build`」,而
|
||||
`mem-budget`(登记簿条目「缩堆迁 CCM 后半(暂缓)」)/ `mem-numbers` 是拿**文档快照 vs `.map`** 比对 —— 门禁跑时
|
||||
`.map` 还是**主工作树合并前**的旧产物,与刚合并进来的新台账**必然不一致** →
|
||||
误报 `[FAIL] 台账受管区与 .map 不一致`。`@build` 跑完后 `.map` 才是新的。
|
||||
误报 `[FAIL] 台账受管区与 .map 不一致`。`/keil-build` 跑完后 `.map` 才是新的。
|
||||
**处置(2026-10-25 补全)**:按 C 节第 5 步的完整链补——`check_mem_numbers.py --write` →
|
||||
`ram_baseline.py --write` → `ram_baseline.py --update` → `check_test_coverage.py --update-baseline` →
|
||||
`check_doc_links.py --all` → 提交 → 重跑 `done`(它会检测到「已合入」直接清场)。
|
||||
@@ -57,15 +57,15 @@
|
||||
- **⚠ 新建工作树从未构建过 → 体积类门禁必然红**(2026-10-25 实测):`.map` 是每棵树各自的
|
||||
构建产物、不入版本库,所以 `new` 出来的空间里 `mem-numbers` / `mem-budget` / `ci_local.py`
|
||||
的体积门禁步都拿不到事实源,报 `[FAIL] 找不到 .map 事实源`(脚本提示已写明原因,不是代码缺陷)。
|
||||
同理 `done` 的重生成步骤会 `[skip] 未找到 .map,跳过内存快照同步(需先 @build)`。
|
||||
**处置**:新空间第一次提交前先 `@build` 一次(`build` 锁自动排队,与别的树串行)。与上一条
|
||||
同理 `done` 的重生成步骤会 `[skip] 未找到 .map,跳过内存快照同步(需先 /keil-build)`。
|
||||
**处置**:新空间第一次提交前先 `/keil-build` 一次(`build` 锁自动排队,与别的树串行)。与上一条
|
||||
的区别要分清:上一条是「有 `.map` 但是旧的」,本条是「根本没有 `.map`」,处置动作不同。
|
||||
- **⚠ 只改注释里的文档路径,也会把「体积同码凭证」刷红**(2026-10-30 实测,`done testdoc`):
|
||||
`scripts/check_size_gate.py` 的判据是**索引 blob 指纹**(实测 1466 个文件),**不是体积数字**——
|
||||
那笔全库路径同步顺带改了 20 个 `.c/.h` 的注释,`Program Size` 逐项没变
|
||||
(Code=246160 / RO-data=29816 / RW-data=2036 / ZI-data=149700),凭证照旧红:
|
||||
`本次代码(指纹 172c6c78b8fb…,1466 文件)与体积测量(指纹 1ed4267c77f6…,2026-10-30T01:15:01)不同码`。
|
||||
**处置比上一条那条补绿链短**:合并后 `done` 自己那次 `@build` 已 0 错 0 警,此时只需
|
||||
**处置比上一条那条补绿链短**:合并后 `done` 自己那次 `/keil-build` 已 0 错 0 警,此时只需
|
||||
`ram_baseline.py --update` 重写凭证(改的是 `scripts/size_attestation.json`;同日同数字时
|
||||
`ram_baseline.tsv` 连趋势行都不会新增),**别顺手 `--write` 去动内存受管快照**——数字没变就改受管区,
|
||||
等于给别的在飞分支造冲突面(台账归属口径见 `mem-budget` skill)。
|
||||
@@ -75,7 +75,7 @@
|
||||
落主干后的全量证据得自己跑:`scripts/ci_local.py`(实测 47 runner / 3896 断言全绿)
|
||||
+ 主树 `check_doc_links.py --all`(实测 215 个文档 / 2993 处章节引用 rc=0)。
|
||||
- **⚠ 增量构建可能不重链 → `.map` 数字瞬态不一致**(2026-10-12 实测):
|
||||
`@build` 若判定无需重链,`.map` 保持旧值,此时 `--write` 生成的快照是**瞬态值**
|
||||
`/keil-build` 若判定无需重链,`.map` 保持旧值,此时 `--write` 生成的快照是**瞬态值**
|
||||
(实测同一份源码两次读到 223,560 与 223,724,差 164 B)。**以主工作树(集成点)
|
||||
的 `.map` 为准**;改完 RAM 相关代码后确认重链再刷新快照。
|
||||
- **`ram_baseline.py` 的 help 串里有未转义的 `%`**(2026-10-25 实测:某些直调姿势会渲染 argparse help 并崩)——
|
||||
@@ -120,15 +120,15 @@
|
||||
同任务重复 acquire 是幂等的(不会把自己锁死)。
|
||||
- **TTL 按锁分设**(2026-10-12):`build` = **1h**、`bench` = **6h**(`LOCK_TTL_HOURS`)。
|
||||
口径是「兜底窗口应与操作真实时长同量级」——编译是秒~分钟级,6h 的兜底太钝;
|
||||
bench 是会话级(flash→`@test`→探针),才配得上小时级。持有者还可用
|
||||
bench 是会话级(flash→`/serial-boot-test`→探针),才配得上小时级。持有者还可用
|
||||
`lock acquire ... --ttl <小时>` **自报**更精确的窗口(`keil_build.py` 即按其 `--timeout` 自报)。
|
||||
- **陈旧锁自动接管**:超过该锁 TTL 未释放即视为陈旧,下次 `acquire` 会接管并提示;
|
||||
未到 TTL 的活锁需 `--force`。`done` 的收口前置也会**自动释放陈旧锁**(不再硬失败),但
|
||||
**未到 TTL 的活锁会把 `done` 挡在前置检查**(B 节第 8 步)。
|
||||
- **`build` 锁不必手工持**(2026-10-12 起):`@build`(`keil_build.py`)**自持自释**——
|
||||
- **`build` 锁不必手工持**(2026-10-12 起):`/keil-build`(`keil_build.py`)**自持自释**——
|
||||
进入 acquire、`finally` 无条件 release,所以"Agent 忘了 release、锁一直挂到 TTL"这条在构建路径上已消除。
|
||||
`done` 的门禁自己持锁并给子进程设 `DTU_LOCK_HELD=build` 告知跳过(新旧脚本都兼容)。
|
||||
**`bench` 锁仍须 Agent 手工持**(会话作用域:跨烧录→`@test`→探针,见 `keil-build` SKILL.md)。
|
||||
**`bench` 锁仍须 Agent 手工持**(会话作用域:跨烧录→`/serial-boot-test`→探针,见 `keil-build` SKILL.md)。
|
||||
- **⚠ `done --dry-run` 只保护预演段、不保护清场段**(2026-10-29 实测):`done <ID> --dry-run --skip-build --skip-gates` 在「分支已合入主干」这条路上**照样执行清场**——摘 `.venv` / `simulator/SDL2` 联接、删分支、清台账,然后打「完成(分支此前已合入)」。想真·零改动就只跑不带 `--skip-gates` 的 `--dry-run` 并停在预演输出;用完一律 `git worktree list` 复验(判完成以 `git log` 为准,不是以有没有再敲一次 `done` 为准)。另:`--dry-run` 的前置检查同样要抢 `build` 锁——被别的任务占着时它会直接报「独占资源 build 仍被 X 占用,先 release」,此时加 `--skip-build` 或先 release,**别为了预演去抢别人的锁**。
|
||||
- **⚠ 文件工具「先读后改」按解析后的绝对路径记账,切树后要重新读**(2026-10-30 实测,屏板链路任务树):开新树后先用**相对路径**读过一批文件(读历史记到当时解析到的主树文件上),再进任务树直接 `edit` → 6 条并行编辑全被 `file has not been read — read the file, then retry` 拒。判据:**树内改动一律用树内绝对路径,先 read 再改**——读历史不跨路径复用,工具不做「同名相对路径」宽容。
|
||||
- **⚠ 中文门禁输出回显乱码 ≠ 路径坏**(2026-10-30 实测):对含中文文件名的文档跑 `check_doc_links.py`,终端回显整段乱码,但 **rc 与判定计数全部正确**。只认 `[SUMMARY]` 行与退出码,别误诊成「文档丢失」;脚本化批量调用走 python subprocess + 显式 `encoding`,别经命令行中转中文名。
|
||||
|
||||
@@ -32,7 +32,7 @@ LOCK_NAMES = ("build", "bench")
|
||||
# 按锁分设陈旧窗口(小时)——兜底窗口应与「操作真实时长」同量级:
|
||||
# build:编译是秒~分钟级(keil_build.py 自持自释,持有窗口 ≈ UV4 运行时长,
|
||||
# 上限 = --timeout,默认 600s);崩溃泄漏时应在数十分钟内即可接管 → 1h。
|
||||
# bench:会话级(flash → @test → 探针,天然几十分钟~数小时)→ 保留 6h。
|
||||
# bench:会话级(flash → /serial-boot-test → 探针,天然几十分钟~数小时)→ 保留 6h。
|
||||
# 另:锁 payload 里持有者可用 `ttl` 字段自报窗口(见 lock_acquire(ttl=...)),
|
||||
# 优先于这里的默认值——这样 build 锁的窗口能自动跟随其 --timeout。
|
||||
LOCK_TTL_HOURS = {"build": 1.0, "bench": 6.0}
|
||||
@@ -404,7 +404,7 @@ def bootstrap(wt: Path):
|
||||
# 插件建的树(.dsh-worktrees/worktree/<name>)缺三样东西,下面的命令逐条补。
|
||||
# 回收**不在这里**:插件树由「/worktree remove|prune」收,done/abort 也不该拿去处理它。
|
||||
# 1. 共享联接——没有 .venv 与 simulator 的 SDL2 SDK,插件树里「.venv\Scripts\python.exe」
|
||||
# 直接找不到解释器:@build、ci_local.py、所有 scripts/check_*.py 全部起跑失败;
|
||||
# 直接找不到解释器:/keil-build、ci_local.py、所有 scripts/check_*.py 全部起跑失败;
|
||||
# 2. 可见性——插件树不在台账里,「list」看不见它,8 个活跃名额也只数登记任务;
|
||||
# 3. 独占资源归属——build 锁由 keil_build.py 自持自释(按 cwd 反推持有者名,
|
||||
# 插件树里算成 pw:<name>),bench 锁仍要人占,占锁名得和「lock status」对得上。
|
||||
@@ -519,7 +519,7 @@ def print_plugin_trees() -> int:
|
||||
def miss(rel):
|
||||
return any(t["jn"].get(rel) == "absent" for t in trees)
|
||||
if miss(".venv"):
|
||||
print(" 缺 .venv:进去跑 @build / ci_local.py 会**静默**落到机器级解释器(无 "
|
||||
print(" 缺 .venv:进去跑 /keil-build / ci_local.py 会**静默**落到机器级解释器(无 "
|
||||
"fontTools,generated-fresh 假红的老根因)")
|
||||
if miss("simulator/SDL2"):
|
||||
print(" 缺 simulator/SDL2:模拟器 build_sim.bat 直接报错——CMakeLists.txt 的 "
|
||||
@@ -562,7 +562,7 @@ def cmd_plugin(a) -> int:
|
||||
print("({} 下没有插件工作树)".format(PLUGIN_SUBDIR))
|
||||
print(" 建法:新会话输入框选「Worktree模式」/ 让模型调 worktree_create / "
|
||||
"/worktree create <name>")
|
||||
print(" 建完回来「plugin adopt <name>」补共享联接,才能在里面跑 @build 与门禁。")
|
||||
print(" 建完回来「plugin adopt <name>」补共享联接,才能在里面跑 /keil-build 与门禁。")
|
||||
return EXIT_OK
|
||||
|
||||
wt = plugin_dir_for(a.name)
|
||||
@@ -584,7 +584,7 @@ def cmd_plugin(a) -> int:
|
||||
print("[warn] 仍缺 {}:主线里该路径不存在或联接失败——先补主线再复跑 adopt"
|
||||
"(这棵树还不能算就绪)".format("、".join(still)))
|
||||
else:
|
||||
print("该目录现在可以跑 @build / ci_local.py / @test")
|
||||
print("该目录现在可以跑 /keil-build / ci_local.py / /serial-boot-test")
|
||||
print("build 锁由 keil_build.py 自持自释,持有者名就是 {}".format(owner))
|
||||
print("上台架前:$TW lock acquire bench --task {} --note <说明>".format(owner))
|
||||
print("下台架后:$TW lock release bench --task {}".format(owner))
|
||||
@@ -606,25 +606,25 @@ def regen(cwd: Path) -> None:
|
||||
if list((Path(cwd) / "MDK-ARM").glob("**/*.map")):
|
||||
run_script("scripts/check_mem_numbers.py", ["--write"], cwd, "同步内存/Flash 快照")
|
||||
else:
|
||||
print("[skip] 未找到 .map,跳过内存快照同步(需先 @build)")
|
||||
print("[skip] 未找到 .map,跳过内存快照同步(需先 /keil-build)")
|
||||
|
||||
|
||||
def run_gates(cwd: Path, skip_build: bool) -> bool:
|
||||
ok = run_script("scripts/ci_local.py", [], cwd, "静态门禁 + 主机侧纯逻辑单测 本地等价 CI")
|
||||
if skip_build:
|
||||
print("[skip] 按 --skip-build 跳过 @build")
|
||||
print("[skip] 按 --skip-build 跳过 /keil-build")
|
||||
return ok
|
||||
got, info = lock_acquire("build", "__done__", wait=900)
|
||||
if not got:
|
||||
print("!! build 锁被 {} 占用(UV4 不能并发构建),跳过 @build".format(info.get("task")))
|
||||
print("!! build 锁被 {} 占用(UV4 不能并发构建),跳过 /keil-build".format(info.get("task")))
|
||||
return False
|
||||
# 告知 @build「调用方已持锁」:新版 keil_build.py 自持自释(见其 docstring),
|
||||
# 告知 /keil-build「调用方已持锁」:新版 keil_build.py 自持自释(见其 docstring),
|
||||
# 读到本变量即跳过,避免自我死锁;**旧版根本不看此变量、也不会自锁**,
|
||||
# 故此处对「工作树里还是旧脚本」的在途任务同样安全(向后兼容)。
|
||||
os.environ["DTU_LOCK_HELD"] = "build"
|
||||
try:
|
||||
if not run_script(".dsh/skills/keil-build/scripts/keil_build.py", [], cwd,
|
||||
"@build(0 错 0 警)"):
|
||||
"/keil-build(0 错 0 警)"):
|
||||
ok = False
|
||||
finally:
|
||||
os.environ.pop("DTU_LOCK_HELD", None)
|
||||
@@ -789,7 +789,7 @@ def cmd_new(a) -> int:
|
||||
print()
|
||||
print("下一步:把新会话的工作目录切到")
|
||||
print(" " + str(wt))
|
||||
print("在该目录里干活、跑 @build 与 ci_local.py(并行安全);")
|
||||
print("在该目录里干活、跑 /keil-build 与 ci_local.py(并行安全);")
|
||||
print("要动台架先 `lock acquire bench --task {}`,完事 `lock release bench --task {} --bench-pass`。".format(
|
||||
a.task_id, a.task_id))
|
||||
print("回收用 `done {}`(**必须在主工作树执行**)。".format(a.task_id))
|
||||
@@ -978,7 +978,7 @@ def cmd_done(a) -> int:
|
||||
return EXIT_OK
|
||||
|
||||
if st.get("needs_bench") and not st.get("bench_pass"):
|
||||
raise Fail("任务标记 --needs-bench 但无台架 PASS 记录;跑完 @test 后执行 "
|
||||
raise Fail("任务标记 --needs-bench 但无台架 PASS 记录;跑完 /serial-boot-test 后执行 "
|
||||
"`lock release bench --task {} --bench-pass`".format(st["id"]))
|
||||
print("[ok] 前置检查通过(领先 {} 共 {} 个提交)".format(base, ahead))
|
||||
|
||||
@@ -1125,7 +1125,7 @@ def cmd_postmerge(a) -> int:
|
||||
if Path.cwd().resolve() != main.resolve():
|
||||
raise EnvError("postmerge 必须在主工作树跑:{}".format(main))
|
||||
if not list((main / "MDK-ARM").glob("**/*.map")):
|
||||
raise EnvError("主树没有 .map——先 @build 0/0 再回来(C.5 的前提)")
|
||||
raise EnvError("主树没有 .map——先 /keil-build 0/0 再回来(C.5 的前提)")
|
||||
gate = "scripts/check_size_gate.py"
|
||||
if not a.force and run_script(gate, ["--local"], main, "体积凭证检查(同码)", tail=8):
|
||||
print("[ok] 凭证同码新鲜,无需补链")
|
||||
@@ -1234,7 +1234,7 @@ def build_parser():
|
||||
p.add_argument("task_id")
|
||||
p.add_argument("--base", default=None)
|
||||
p.add_argument("--dry-run", action="store_true", help="只预演,不做任何改动")
|
||||
p.add_argument("--skip-build", action="store_true", help="跳过 @build")
|
||||
p.add_argument("--skip-build", action="store_true", help="跳过 /keil-build")
|
||||
p.add_argument("--skip-gates", action="store_true", help="跳过全部门禁")
|
||||
p.add_argument("--force", action="store_true", help="清场时强制移除工作树")
|
||||
p.set_defaults(func=cmd_done)
|
||||
|
||||
Reference in New Issue
Block a user