Files
Unlucky-Launcher/.github/workflows/release-guard.yml
T
worker-updatee2e 68a1f90828 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)。
2026-11-08 11:12:11 +08:00

69 lines
4.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Release guard
# 发布标记语义守门(M163):**push tag 默认创建稳定版**这条不许只靠注释保证。
#
# 判据全部在 `packaging/check_release_semantics.py`,它做四件事:
# ① 标记表:push tag(空串)的 create **不含** --prerelease;dispatch true ⇒ 预发布;
# dispatch false ⇒ create 稳定、edit 显式摘标记;非法取值必须抛。
# ② 真 bash 往返:把 `packaging/release_marker.py --emit-bash` 的产物 source 进 bash,
# 读回 create_mark / edit_mark 两个数组——证明流水线**真跑的那段代码**对,不只是函数对。
# ③ release-publish.yml 的可执行内容里没有 --prerelease 字面量:标记只能从那张表来。
# ④ release.yml 的 dispatch 默认值 == false(读 YAML 结构,不是 grep 字符串)。
# 最后一步 `--self-mutation` 是这条守门自己的变异自测:把「默认值改回 true」「塞回字面量」
# 「表里 push 分支改成预发布」逐项注入临时副本,断言守门**每一项都红**、基线绿。守门自己
# 抓不住回归时它会先红,所以这条断言不会变成装饰品。
#
# 为什么不并进 guards.yml:那份文件已经 398 行(400 行红线),再挂一个 job 必越线。
# 改 `release.yml` 的 prerelease 输入 / 改那段 `gh release` 时,先跑
# `python3 packaging/check_release_semantics.py`(本机可用 `python`,脚本会自己找 bash)。
#
# (M164)同一个 job 还挂**发布名单守卫** `packaging/check_publish_slots.py`:`PUBLISH_SLOTS` 与槽位表/产物
# 必须对得上。PR 阶段跑源码级断言(名单非空、每项都是 `release.yml` 槽位表里的 `mode: ci` 档)——
# 名单里写错一个字此前只会表现为「Release 侧一个也不匹配、齐全性检查红在别处」,根因要翻两层日志;
# `--self-test` 再注入 8 条回归(缺资产 / 拼错 / 声明了 probe 档 / 名单声明了没产物 / 有产物没声明)证明它会红。
# 发布侧那道「产物 ⇄ 名单」的运行时断言在 `release-publish.yml`(同一份脚本,`--dist`)。
#
# (M169)同一个 job 再挂**测试通道守门** `packaging/check_test_source_off.py`:core/cli/presenter/
# app-iced 各有一条 `update-test-source` feature,打开后 `core::update::job::default_source` 可以被
# 两个环境变量指向任意本地目录——而更新链路的终点是**替换用户盘上的 exe**。它默认关,官方发布产物
# 里不许有它:守门扫 `.github/workflows/**`(含复合 action)、`packaging/**` 递归脚本、
# `Makefile`/`justfile`/`Taskfile` 与各 `Cargo.toml`(`default` 里带上它也红,那种开法扫 workflow
# 是看不见的)。**唯一允许**是 `ci.yml` 里名为 `update-e2e` 的那一步(且必须带 `--test
# update_e2e_test`,文件名/步骤名/参数任一不符即红)。`--self-test` 注入 14 条真实回归(allowlist
# 六向量 + 七条绕过 + Cargo 两条),该红的红、该绿的绿。
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
release-semantics:
name: 发布标记语义守门(push tag → 稳定版)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 标记表自检(push tag ⇒ 稳定版)
run: python3 packaging/release_marker.py --self-test
- name: 语义断言(标记表 / bash 往返 / 流水线只用表 / dispatch 默认值)
run: python3 packaging/check_release_semantics.py
- name: 变异自测(守门抓不抓得住回归)
run: python3 packaging/check_release_semantics.py --self-mutation
- name: 发布名单守卫自测(声明⇒必须有产物 / 有产物没声明⇒显式警告)
run: python3 packaging/check_publish_slots.py --self-test
- name: 发布名单与矩阵槽位一致(拼错字 / 声明了 probe 档 当场红)
run: python3 packaging/check_publish_slots.py
# M169:`update-test-source` 这条 feature 能让更新源指向任意本地目录,而更新链路的终点是
# 替换用户盘上的 exe。它**默认关**、官方发布产物里不许有它,这条由脚本自动断言(不再靠注释):
# 扫 `.github/workflows/**`(含复合 action)、`packaging/**` 递归脚本、`Makefile`/`justfile`/
# `Taskfile` 与各 `Cargo.toml`;归属按 `jobs.*.steps[]` 的**列表项**判,落不到具名步骤的行一律红。
# 唯一允许:本仓 `ci.yml` 里名为 `update-e2e` 的那一步(且必须带 `--test update_e2e_test`)。
- name: 测试通道守门自测(注入变异,该红的红、该绿的绿)
run: python3 packaging/check_test_source_off.py --self-test
- name: 发布产物不含测试通道(workflow / 打包脚本 / Cargo default 都不许开)
run: python3 packaging/check_test_source_off.py