fix(m169): 复审 r2 —— 守门归属改成「列表项结构判定 + 默认拒绝」,堵掉无名步骤/无名 job/flow 步骤的继承放行
r2 复审(reviewer-updatee2e-r2,p2-4items;被抓出一个真洞,mission 翻回 active)逐条落地。 P2-1(本轮核心,真洞)allowlist 的归属原来按「最近一行 `- name:`」切块,于是任何不写成 `- name: <x>` 的步骤都继承上一块的名字;被放行的 `update-e2e` 恰好是 ci.yml 里最后一个 `- name:`,所以追加到文件末尾的一切都落在它的块里。复审三条真实注入(无名 `- run:`、无名步骤的 整个 job、flow 风格步骤)当时全绿。现在: - `split_items()` 按 YAML **列表项**切块(每个 `- ` 起一项;比当前项深的行归当前项;同级或更浅的 非列表行**结束**当前项)。项名 = 该项内**同级**的 `name:` 键(键序无关;`run: |` 块里的 `name: dummy` 不算;`run: echo "name: x"` 也不算)。 - **默认拒绝**:落不到列表项上的行(`jobs:` 之前的孤儿行、job 级 `run:` 这类项外键)带 feature 即红; 没有 `name:` 的项带 feature 即红。不再有「继承上一块」这回事。 - 三条复审注入 + 本轮新增三条(`jobs:` 之前的孤儿行、`run: |` 里夹带并伪造 name、shell 变量拼接) 都进了自测向量;在**真仓库文件**上重跑也是全红。 P2-2(同族误杀): - 键序无关:允许的那一步把 `run:` 写在 `name:` 之前仍绿(旧实现会报到上一块的名字上)。 - `cargo hack check --all-targets` 不再被误判成 `--all`(正则收紧到 `--all(?![-\w])`)。 - Cargo 的 `update-test-source = [ … ]` **跨行声明**不再被误判成「写进了别的 feature 列表」 (`scan_manifest` 增加 `in_declaration` 状态)。 P2-3 `Fixture::run` 的第二条断言比错对象:原来比夹具字段 `self.dest`,手写 `fx.run(&["apply","--dest","<测试二进制>","--json"])` 两条都过。现在从 `args` 里解析真实 `--dest` 值(含 `--dest=` 形式)再 canonicalize 比较;夹具字段那份作为额外一道保留。 P2-4 `release-guard.yml` 的步骤级注释还停在 r1 的扫描面:改成「含复合 action / packaging 递归 / Makefile 等 + 归属按列表项判 + 唯一允许 ci.yml 的 update-e2e 一步」。 自测数据搬到 `packaging/tests/guard_vectors.json`(基线 8 个文件 + 26 条向量:21 红 / 6 绿), 脚本本身从 379 行降到 373 行(守门仍 ≤400);README §14 补上归属规则与「抓不住」清单。 验证(本机,全部 exit 0): - 守门正式扫描绿;`--self-test` 21 条注入全红 + 6 条基线/放行绿;向量文件缺失时 exit 1(不静默)。 - **真仓注入**(在真 ci.yml / release-build.yml / cli/Cargo.toml 上改完即还原):复审三条(无名步骤 / 无名 job / flow 步骤)全红;本轮新增三条(孤儿行 / `run: |` 夹带 / shell 拼接)全红; 三条合法写法(键序、`--all-targets`、跨行声明)全绿;还原后基线绿。 - E2E:debug 6 绿、release 6 绿(`running 3 tests` ×2,0.26s / 0.23s,非 0 measured)。 - `cargo clippy -p unlucky-cli --all-targets --features update-test-source -- -D warnings` 干净; `cargo fmt --all -- --check` 干净;全仓 400 行扫描无超限(守门 373 / 向量 194 / ci.yml 317)。 约束遵守:未 push;`PUBLISH_SLOTS` 只列 `windows-x64`(未动);release 侧仍无该 feature;跨卷 bug 未碰。 ⚠ 仍未验:CI 里 E2E 真跑起来本机验不了(只证明 workflow 写对 + 守门放行,首跑要等推送后看 Actions)。
This commit is contained in:
1 parent
28bf104041
commit
68a1f90828
5 files changed
+389
-178
No files matched your search
@@ -255,25 +255,40 @@ impl Fixture {
|
||||
|
||||
/// 跑一条 `update` 子命令:`--data-dir` 指向临时数据根(与 GUI 一样由数据根派生暂存目录)。
|
||||
///
|
||||
/// **结构性保险(r1 复审 P2-5)**:`apply` 必须显式带 `--dest`,且 `--dest` 不许是本进程的 exe。
|
||||
/// 不带 `--dest` 就是 `Job::for_process` 的「本进程 exe」——在这套用例里那是**测试二进制**,
|
||||
/// 而 Windows 允许改名正在运行的映像,于是整轮测试会把它换成一个假启动器(症状极难查)。
|
||||
/// **结构性保险(r1 复审 P2-5 / r2 复审 P2-3)**:`apply` 必须显式带 `--dest`,且**这一次调用实际
|
||||
/// 传下去的那个 `--dest` 值**不许是本进程的 exe。不带 `--dest` 就是 `Job::for_process` 的「本进程
|
||||
/// exe」——在这套用例里那是**测试二进制**,而 Windows 允许改名正在运行的映像,于是整轮测试会把它
|
||||
/// 换成一个假启动器(症状极难查)。
|
||||
///
|
||||
/// 抓得住:以后有人手写 `fx.run(&["apply", "--json"])`(漏 `--dest`),或把 dest 指到
|
||||
/// `current_exe()`——**当场 panic**,现场不会被破坏。
|
||||
/// 比的是 **args 里那个 `--dest` 的值**(含 `--dest=<path>` 形式),不是夹具字段:r2 复审指出
|
||||
/// 「比 `self.dest`」等于只守住了夹具自己那一份,手写
|
||||
/// `fx.run(&["apply", "--dest", "<测试二进制>", "--json"])` 照样能过。夹具字段那一份作为额外一道
|
||||
/// 保留(`Fixture::apply()` 走的就是它)。
|
||||
///
|
||||
/// 抓得住:漏 `--dest`、把 `--dest` 指到本进程 exe、夹具 dest 被改成 exe——都**当场 panic**。
|
||||
/// 抓不住:绕开 `Fixture` 直接调 `unlucky_cli::run`(那等于另写一套夹具,`--data-dir` 也没人拼了)。
|
||||
pub fn run(&self, args: &[&str]) -> (i32, String, String) {
|
||||
let requested = args
|
||||
.iter()
|
||||
.position(|arg| *arg == "--dest")
|
||||
.and_then(|index| args.get(index + 1).copied())
|
||||
.or_else(|| args.iter().find_map(|arg| arg.strip_prefix("--dest=")));
|
||||
if args.first() == Some(&"apply") {
|
||||
assert!(
|
||||
args.contains(&"--dest"),
|
||||
requested.is_some(),
|
||||
"apply 必须带 --dest:不带会替换本进程 exe(这套用例里就是测试二进制)。\
|
||||
唯一的 apply 入口是 `Fixture::apply()`"
|
||||
);
|
||||
}
|
||||
let dest = std::fs::canonicalize(&self.dest)
|
||||
.expect("dest 必须存在(Fixture::new 写入了 INSTALLED)");
|
||||
if let Ok(exe) = std::env::current_exe().and_then(std::fs::canonicalize) {
|
||||
assert_ne!(dest, exe, "替换目标不许是本进程的 exe");
|
||||
let asked = requested.and_then(|path| std::fs::canonicalize(path).ok());
|
||||
let fixture = std::fs::canonicalize(&self.dest).ok();
|
||||
for dest in [asked, fixture].into_iter().flatten() {
|
||||
assert_ne!(
|
||||
dest, exe,
|
||||
"替换目标不许是本进程的 exe(`--dest` 点的就是它)"
|
||||
);
|
||||
}
|
||||
}
|
||||
let mut full = vec!["update"];
|
||||
full.extend_from_slice(args);
|
||||
|
||||
Reference in new issue
Block a user