fix(m169): r3 四条就地修完 —— 只认同级 name:、纳入 .cargo/config.toml、登记 env 交接边界、校准两处数字
合并门禁只认 clean,r3 的 p2-4items 就地修掉(无行为改动,判据更严,不放松任何一条)。 P2-1(一行 + 三向量)`_name_of` 现在只认**同级** `name:`(`item.end() == key_indent`):更深缩进的 `- name:`(`env:`/`with:` 子列表、`run: |` 块标量里的行)不再被当成项名;同一项里出现两个**不同** 的 `name:` 返回 `None`(无法判定 ⇒ 默认拒绝,不去猜 YAML 重复键语义)。新向量:name 藏在 env 子项 / 藏在 `run: |` 里 / 两个不同 name ⇒ 必须红。 P2-2 `.cargo/config*.toml` 进扫描面(`CONFIG_GLOBS`:根与 `crates/*` 下的 `config.toml`/`config`), 新增 `scan_cargo_config`:feature 字面量(覆盖 `--cfg feature="…"`、`[env] RUSTFLAGS`)走原有判据, `rustc-wrapper` / `rustc-workspace-wrapper` **一律判红**(wrapper 注入什么由程序决定,文本扫描看不见; 合法用途如 sccache 需要 review 明确同意,不能顺手加)。新向量:config.toml 用 `--cfg` 开该 feature ⇒ 红;设 rustc-wrapper ⇒ 红;普通 config.toml ⇒ 绿(防误杀)。 P2-3 运行期 env 交接写进守门自己的「抓不住」表(脚本头 + README §14):第一步把 feature 写进 `$GITHUB_ENV`、第二步只用 `--features "$FEAT"`——第二步没有字面量,**任何静态扫描都看不见**,这是本 守门的固有边界(引 YAML 解析器也修不了,因为这是数据流不是结构),不是漏判。 P2-4 两处过期数字:`release-guard.yml` 的「注入 14 条」→ 26 条注入 + 6 条防误杀/放行(另加基线, 条数以 `--self-test` 输出为准);README 的「26 条:21 红 + 6 绿」→「32 条向量:26 红 + 6 绿 + 基线」。 守门脚本 399 行(≤400,仍零第三方依赖);自测数据 32 条向量在 `packaging/tests/guard_vectors.json`。 验证(本机,未跑全量 cargo —— tower 已在合并态跑过 workspace 全量 EXIT=0/2648 passed): - `--self-test`:26 条注入全红 + 7 条基线/防误杀绿,exit 0。 - **真仓注入 8 组**(改真 ci.yml / 新建真 .cargo/config.toml,改完即还原):env 子项里的 name ⇒ 红; `run: |` 里的 name ⇒ 红;两个不同 name ⇒ 红;`--cfg feature=` ⇒ 红;`rustc-wrapper` ⇒ 红; 普通 config.toml ⇒ 绿;r2 的无名步骤 / flow 步骤回归 ⇒ 仍红;还原后基线绿。 - 正式扫描(真仓)exit 0;全仓 400 行扫描无超限;改动文件全 LF。
This commit is contained in:
1 parent
68a1f90828
commit
d7adbd1172
4 files changed
+110
-37
No files matched your search
@@ -27,10 +27,11 @@ name: Release guard
|
|||||||
# app-iced 各有一条 `update-test-source` feature,打开后 `core::update::job::default_source` 可以被
|
# app-iced 各有一条 `update-test-source` feature,打开后 `core::update::job::default_source` 可以被
|
||||||
# 两个环境变量指向任意本地目录——而更新链路的终点是**替换用户盘上的 exe**。它默认关,官方发布产物
|
# 两个环境变量指向任意本地目录——而更新链路的终点是**替换用户盘上的 exe**。它默认关,官方发布产物
|
||||||
# 里不许有它:守门扫 `.github/workflows/**`(含复合 action)、`packaging/**` 递归脚本、
|
# 里不许有它:守门扫 `.github/workflows/**`(含复合 action)、`packaging/**` 递归脚本、
|
||||||
# `Makefile`/`justfile`/`Taskfile` 与各 `Cargo.toml`(`default` 里带上它也红,那种开法扫 workflow
|
# `Makefile`/`justfile`/`Taskfile`、各 `Cargo.toml`(`default` 里带上它也红)与 `.cargo/config*.toml`
|
||||||
# 是看不见的)。**唯一允许**是 `ci.yml` 里名为 `update-e2e` 的那一步(且必须带 `--test
|
# (`--cfg feature="…"` 与 `rustc-wrapper` 都能绕过 `--features`)。**唯一允许**是 `ci.yml` 里名为
|
||||||
# update_e2e_test`,文件名/步骤名/参数任一不符即红)。`--self-test` 注入 14 条真实回归(allowlist
|
# `update-e2e` 的那一步(且必须带 `--test update_e2e_test`,文件名/步骤名/参数任一不符即红)。
|
||||||
# 六向量 + 七条绕过 + Cargo 两条),该红的红、该绿的绿。
|
# `--self-test` 注入 26 条真实回归 + 6 条防误杀/放行用例(另加基线;条数以 `--self-test` 输出为准),
|
||||||
|
# 该红的红、该绿的绿。
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|||||||
+8
-4
@@ -66,7 +66,7 @@ Git Bash 下会静默落进 Docker VM(脚本内部用 `cygpath -m` 归一)
|
|||||||
| `packaging/check_screenshot.py` | 截图非空白判定(CI 门禁 + 本地手动跑,零第三方依赖) | ≤400 |
|
| `packaging/check_screenshot.py` | 截图非空白判定(CI 门禁 + 本地手动跑,零第三方依赖) | ≤400 |
|
||||||
| `packaging/check_publish_slots.py` | 发布名单守卫(M164):名单 ⇄ 产物两个方向;`--self-test` 注入 8 条回归 | ≤400 |
|
| `packaging/check_publish_slots.py` | 发布名单守卫(M164):名单 ⇄ 产物两个方向;`--self-test` 注入 8 条回归 | ≤400 |
|
||||||
| `packaging/check_test_source_off.py` | 测试通道守门(M169):workflow / 打包脚本 / `Cargo.toml default` 都不许打开 `update-test-source`;归属按 `jobs.*.steps[]` 列表项判、默认拒绝;`--self-test` 按 `tests/guard_vectors.json` 注入 | ≤400 |
|
| `packaging/check_test_source_off.py` | 测试通道守门(M169):workflow / 打包脚本 / `Cargo.toml default` 都不许打开 `update-test-source`;归属按 `jobs.*.steps[]` 列表项判、默认拒绝;`--self-test` 按 `tests/guard_vectors.json` 注入 | ≤400 |
|
||||||
| `packaging/tests/guard_vectors.json` | 守门自测向量(基线 + 26 条:21 红 / 6 绿),数据与代码分开 | ≤400 |
|
| `packaging/tests/guard_vectors.json` | 守门自测向量(基线 + 32 条:26 红 / 6 绿),数据与代码分开 | ≤400 |
|
||||||
| `packaging/ci-runtime-smoke.md` | §11 的细节:断言口径/阈值依据/失效模式/复跑 | ≤400 |
|
| `packaging/ci-runtime-smoke.md` | §11 的细节:断言口径/阈值依据/失效模式/复跑 | ≤400 |
|
||||||
| `packaging/linux-smoke-docker.sh` | 本地复跑 `runtime-smoke`(Docker + xvfb) | ≤400 |
|
| `packaging/linux-smoke-docker.sh` | 本地复跑 `runtime-smoke`(Docker + xvfb) | ≤400 |
|
||||||
| `packaging/linux-check-docker.sh` | 本地复跑 `ci.yml` 的两条 ubuntu 门禁(clippy / test,Docker) | ≤400 |
|
| `packaging/linux-check-docker.sh` | 本地复跑 `ci.yml` 的两条 ubuntu 门禁(clippy / test,Docker) | ≤400 |
|
||||||
@@ -129,9 +129,13 @@ Git Bash 下会静默落进 Docker VM(脚本内部用 `cygpath -m` 归一)
|
|||||||
(文件名 / 步骤名 / 参数任一不符即红,见脚本的 `ALLOWED_STEP`)。
|
(文件名 / 步骤名 / 参数任一不符即红,见脚本的 `ALLOWED_STEP`)。
|
||||||
归属按 **`jobs.*.steps[]` 的列表项**判(不是「最近一行 `- name:`」):落不到具名步骤上的行——`jobs:`
|
归属按 **`jobs.*.steps[]` 的列表项**判(不是「最近一行 `- name:`」):落不到具名步骤上的行——`jobs:`
|
||||||
之前的孤儿行、项外键、没有 `name:` 的步骤、flow 风格步骤——**一律判红**(默认拒绝)。自测向量在
|
之前的孤儿行、项外键、没有 `name:` 的步骤、flow 风格步骤——**一律判红**(默认拒绝)。自测向量在
|
||||||
`packaging/tests/guard_vectors.json`(26 条:21 条注入必须红 + 6 条基线/合法写法必须绿)。
|
`packaging/tests/guard_vectors.json`(32 条向量:26 条注入必须红 + 6 条合法写法必须绿,另加基线必须绿;
|
||||||
抓不住(如实记):变量间接拼接(`FEAT="update-""test-source"`)、feature 改名后接回发布、`build.rs`
|
条数以 `--self-test` 输出为准)。`.cargo/config*.toml` 也在扫描面里:`--cfg feature="…"` 与
|
||||||
里 spawn cargo、以及「把别的步骤**改名**成 `update-e2e` 并带 `--test update_e2e_test` 却干别的事」。
|
`rustc-wrapper` 都能绕过 `--features`(后者一律判红,合法用途要 review 明确同意)。
|
||||||
|
抓不住(如实记):变量间接拼接(`FEAT="update-""test-source"`)、**两步 `$GITHUB_ENV` 交接**
|
||||||
|
(第一步把 feature 写进 env、第二步只用 `--features "$FEAT"`——第二步没有字面量,任何静态扫描都
|
||||||
|
看不见,这是本守门的固有边界,不是漏判)、feature 改名后接回发布、`build.rs` 里 spawn cargo、以及
|
||||||
|
「把别的步骤**改名**成 `update-e2e` 并带 `--test update_e2e_test` 却干别的事」。
|
||||||
- 验收本体:`cargo test --release -p unlucky-cli --features update-test-source --test update_e2e_test
|
- 验收本体:`cargo test --release -p unlucky-cli --features update-test-source --test update_e2e_test
|
||||||
--test update_e2e_tamper_test`(正路 + 幂等 / 篡改摘要 + 篡改包体 + 截断)。CI 里同一个命令挂在
|
--test update_e2e_tamper_test`(正路 + 幂等 / 篡改摘要 + 篡改包体 + 截断)。CI 里同一个命令挂在
|
||||||
`ci.yml` 的 `update-e2e` job(**只在 Windows**:夹具的「真形状」是 Windows 裸 `.exe` +
|
`ci.yml` 的 `update-e2e` job(**只在 Windows**:夹具的「真形状」是 Windows 裸 `.exe` +
|
||||||
|
|||||||
@@ -15,16 +15,17 @@
|
|||||||
2. **不许「打开所有 feature」**:`--all-features`、`cargo hack --feature-powerset|--each-feature|--all`。
|
2. **不许「打开所有 feature」**:`--all-features`、`cargo hack --feature-powerset|--each-feature|--all`。
|
||||||
3. **不许设成默认**:`Cargo.toml` 里它只许是**自己的声明**(含跨行数组);进 `default` 或别的
|
3. **不许设成默认**:`Cargo.toml` 里它只许是**自己的声明**(含跨行数组);进 `default` 或别的
|
||||||
feature 的依赖数组即红。
|
feature 的依赖数组即红。
|
||||||
4. **扫描面逐项列举**(`WORKFLOW_GLOBS` / `SCRIPT_GLOBS` / `BUILD_ENTRY_GLOBS`):含
|
4. **扫描面逐项列举**(`WORKFLOW_GLOBS` / `SCRIPT_GLOBS` / `BUILD_ENTRY_GLOBS` / `CONFIG_GLOBS`):
|
||||||
`.github/actions/**`、`packaging/**` 递归子目录、`Makefile`/`justfile`/`Taskfile`。
|
含 `.github/actions/**`、`packaging/**` 递归子目录、`Makefile`/`justfile`/`Taskfile`、以及
|
||||||
|
`.cargo/config*.toml`(`--cfg feature="…"` 与 `rustc-wrapper` 都能绕过 `--features`,见
|
||||||
|
`scan_cargo_config`)。
|
||||||
|
|
||||||
注释剥离是**引号感知**的(`echo "#"; cargo build --features …` 里那个 `#` 不是注释)。
|
注释剥离是**引号感知**的(`echo "#"; cargo build --features …` 里那个 `#` 不是注释)。抓不住的都是
|
||||||
抓得住/抓不住 的完整清单见 `packaging/README.md` §14。要点:变量间接拼接(`FEAT="update-""test-source"`)、
|
**故意手打**或**运行期数据流**(完整清单见 `packaging/README.md` §14):变量拼接、两步 `$GITHUB_ENV`
|
||||||
feature 改名后接回发布、`build.rs` 里 spawn cargo、以及「有人把别的步骤**改名**成 `update-e2e` 并带
|
交接(第二步没有字面量——静态扫描的固有边界)、feature 改名后接回、`build.rs` 里 spawn cargo、改名复用。
|
||||||
`--test update_e2e_test` 却干别的事」都抓不住(扫描只看字符串;merge review 会看到 ci.yml 那一行)。
|
|
||||||
|
|
||||||
`--self-test` 把每条断言的**真实回归**注入临时副本,断言该红的红、该绿的绿(含 r2 的三条绕过与四条
|
`--self-test` 把每条断言的**真实回归**注入临时副本(数据在 `packaging/tests/guard_vectors.json`),
|
||||||
误杀回归)。守门自己抓不住回归时它会先红。
|
断言该红的红、该绿的绿。守门自己抓不住回归时它会先红。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -59,6 +60,15 @@ BUILD_ENTRY_GLOBS = (
|
|||||||
"packaging/Makefile",
|
"packaging/Makefile",
|
||||||
)
|
)
|
||||||
MANIFEST_GLOBS = ("Cargo.toml", "crates/*/Cargo.toml")
|
MANIFEST_GLOBS = ("Cargo.toml", "crates/*/Cargo.toml")
|
||||||
|
#: cargo 配置:`--cfg feature="…"` 与 `rustc-wrapper` 都能在没有 `--features` 时打开这条通道(P2-2)。
|
||||||
|
CONFIG_GLOBS = (
|
||||||
|
".cargo/config.toml",
|
||||||
|
".cargo/config",
|
||||||
|
"crates/*/.cargo/config.toml",
|
||||||
|
"crates/*/.cargo/config",
|
||||||
|
)
|
||||||
|
#: 任意 wrapper 都能注入 `--cfg`,文本扫描看不见 ⇒ 一律判红。
|
||||||
|
WRAPPER_KEY = re.compile(r"^\s*(rustc|rustc-workspace)-wrapper\s*=")
|
||||||
#: 唯一允许出现该 feature 的地方:**文件 + 列表项名 + 必须同在的字符串**,三者不符即红。
|
#: 唯一允许出现该 feature 的地方:**文件 + 列表项名 + 必须同在的字符串**,三者不符即红。
|
||||||
ALLOWED_STEP = {
|
ALLOWED_STEP = {
|
||||||
"workflow": ".github/workflows/ci.yml",
|
"workflow": ".github/workflows/ci.yml",
|
||||||
@@ -112,10 +122,12 @@ def _lines(path: Path) -> list[tuple[int, str]]:
|
|||||||
def _name_of(key_indent: int, body: list[tuple[int, str]]) -> str | None:
|
def _name_of(key_indent: int, body: list[tuple[int, str]]) -> str | None:
|
||||||
"""列表项的名字:项内**同级**的 `name:` 键(键序无关;块标量内容不当键)。
|
"""列表项的名字:项内**同级**的 `name:` 键(键序无关;块标量内容不当键)。
|
||||||
|
|
||||||
找不到 → `None` ⇒ 这一项落不到具名步骤 ⇒ 带 feature 就红(默认拒绝)。只认键位置上的
|
「同级」= 项首行 `- ` 之后那一列(`item.end() == key_indent`,r3 复审 P2-1):更深缩进的
|
||||||
`name:`:`run: echo "name: x"` 这种不算,`run: |` 块里的 `name: dummy` 也不算。
|
`- name:`(`with:` / `env:` 子列表、`run: |` 内容)**不算**项名。找不到、或出现两个**不同**的
|
||||||
|
`name:` 都返回 `None` ⇒ 落不到具名步骤 ⇒ 带 feature 就红(默认拒绝)。
|
||||||
"""
|
"""
|
||||||
in_block = False
|
in_block = False
|
||||||
|
found: str | None = None
|
||||||
for _number, line in body:
|
for _number, line in body:
|
||||||
text = line.strip()
|
text = line.strip()
|
||||||
if not text or text.startswith("#"):
|
if not text or text.startswith("#"):
|
||||||
@@ -125,19 +137,23 @@ def _name_of(key_indent: int, body: list[tuple[int, str]]) -> str | None:
|
|||||||
if in_block and indent > key_indent:
|
if in_block and indent > key_indent:
|
||||||
continue
|
continue
|
||||||
in_block = False
|
in_block = False
|
||||||
if item:
|
if item and item.end() == key_indent:
|
||||||
code = strip_comment(line[item.end() :]) # 项首行:` - name: x` → `name: x`
|
code = strip_comment(line[item.end() :]) # 项首行:` - name: x` → `name: x`
|
||||||
elif indent == key_indent:
|
elif not item and indent == key_indent:
|
||||||
code = strip_comment(text)
|
code = strip_comment(text)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
for pattern in (NAME_BLOCK, NAME_FLOW):
|
for pattern in (NAME_BLOCK, NAME_FLOW):
|
||||||
match = pattern.match(code.strip())
|
match = pattern.match(code.strip())
|
||||||
if match:
|
if match:
|
||||||
return match.group(1).strip().rstrip("}").strip("'\"")
|
value = match.group(1).strip().rstrip("}").strip("'\"")
|
||||||
|
if found is not None and found != value:
|
||||||
|
return None
|
||||||
|
found = value
|
||||||
|
break
|
||||||
if code.rstrip().endswith(BLOCK_SCALAR):
|
if code.rstrip().endswith(BLOCK_SCALAR):
|
||||||
in_block = True
|
in_block = True
|
||||||
return None
|
return found
|
||||||
|
|
||||||
|
|
||||||
def split_items(
|
def split_items(
|
||||||
@@ -145,8 +161,8 @@ def split_items(
|
|||||||
) -> tuple[list[tuple[str | None, list[tuple[int, str]]]], list[tuple[int, str]]]:
|
) -> tuple[list[tuple[str | None, list[tuple[int, str]]]], list[tuple[int, str]]]:
|
||||||
"""按 YAML **列表项**切块,返回((项名, 该项的行), 孤儿行)。
|
"""按 YAML **列表项**切块,返回((项名, 该项的行), 孤儿行)。
|
||||||
|
|
||||||
每个 `- ` 起一项;比当前项更深的行归当前项,**同级或更浅的非列表行结束当前项**(那是 job 键 /
|
每个 `- ` 起一项;更深的行归当前项,**同级或更浅的非列表行结束当前项**(job 键 / `steps:` /
|
||||||
`steps:` / 顶层键)。落不到列表项上的行是孤儿——不属于任何步骤,带 feature 就红。
|
顶层键)。落不到列表项上的行是孤儿——不属于任何步骤,带 feature 就红。
|
||||||
"""
|
"""
|
||||||
items: list[tuple[str | None, list[tuple[int, str]]]] = []
|
items: list[tuple[str | None, list[tuple[int, str]]]] = []
|
||||||
orphans: list[tuple[int, str]] = []
|
orphans: list[tuple[int, str]] = []
|
||||||
@@ -179,20 +195,11 @@ def split_items(
|
|||||||
def _reason(where: str, name: str | None, text: str) -> str | None:
|
def _reason(where: str, name: str | None, text: str) -> str | None:
|
||||||
"""`None` = 这一项被放行;否则返回「为什么红」的一句话(按最具体的那条给)。"""
|
"""`None` = 这一项被放行;否则返回「为什么红」的一句话(按最具体的那条给)。"""
|
||||||
if where != ALLOWED_STEP["workflow"]:
|
if where != ALLOWED_STEP["workflow"]:
|
||||||
return (
|
return f"`{FEATURE}` 只许出现在 `{ALLOWED_STEP['workflow']}` 的 `{ALLOWED_STEP['name']}` 步骤里(这里是 `{where}`)"
|
||||||
f"`{FEATURE}` 只许出现在 `{ALLOWED_STEP['workflow']}` 的 "
|
|
||||||
f"`{ALLOWED_STEP['name']}` 步骤里(这里是 `{where}`)"
|
|
||||||
)
|
|
||||||
if name is None:
|
if name is None:
|
||||||
return (
|
return f"这一项没有同级 `name:`,落不到具名步骤(默认拒绝);`{where}` 里只有 `{ALLOWED_STEP['name']}` 可以用它"
|
||||||
f"这一项没有 `name:`,落不到具名步骤(默认拒绝);`{where}` 里只有名为 "
|
|
||||||
f"`{ALLOWED_STEP['name']}` 的步骤可以用它"
|
|
||||||
)
|
|
||||||
if name != ALLOWED_STEP["name"]:
|
if name != ALLOWED_STEP["name"]:
|
||||||
return (
|
return f"`{where}` 里只有名为 `{ALLOWED_STEP['name']}` 的步骤可以用 `{FEATURE}`(这一项叫 `{name}`)"
|
||||||
f"`{where}` 里只有名为 `{ALLOWED_STEP['name']}` 的步骤可以用 `{FEATURE}`"
|
|
||||||
f"(这一项叫 `{name}`)"
|
|
||||||
)
|
|
||||||
if ALLOWED_STEP["must_contain"] not in text:
|
if ALLOWED_STEP["must_contain"] not in text:
|
||||||
return f"`{where}` 的 `{name}` 步骤少了 `{ALLOWED_STEP['must_contain']}`"
|
return f"`{where}` 的 `{name}` 步骤少了 `{ALLOWED_STEP['must_contain']}`"
|
||||||
return None
|
return None
|
||||||
@@ -238,7 +245,7 @@ def scan_workflow(path: Path, root: Path) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
def scan_code_file(path: Path, root: Path) -> list[str]:
|
def scan_code_file(path: Path, root: Path) -> list[str]:
|
||||||
"""扫脚本 / 第二构建入口:没有 allowlist,字面量或开关出现即红。"""
|
"""扫脚本 / 构建入口 / cargo config:没有 allowlist,字面量或开关出现即红。"""
|
||||||
where = path.relative_to(root).as_posix()
|
where = path.relative_to(root).as_posix()
|
||||||
problems: list[str] = []
|
problems: list[str] = []
|
||||||
for number, line in _lines(path):
|
for number, line in _lines(path):
|
||||||
@@ -280,6 +287,24 @@ def scan_manifest(path: Path, root: Path) -> list[str]:
|
|||||||
return problems
|
return problems
|
||||||
|
|
||||||
|
|
||||||
|
def scan_cargo_config(path: Path, root: Path) -> list[str]:
|
||||||
|
"""扫 `.cargo/config*.toml`:`--cfg feature="…"` 与 `rustc-wrapper` 都能绕过 `--features`。
|
||||||
|
|
||||||
|
r3 复审 P2-2 实测:`RUSTFLAGS='--cfg=feature="update-test-source"'` 能让 `#[cfg(feature = …)]`
|
||||||
|
段在没有 `--features` 时编进来。`[build] rustflags` / `[env]` 靠 feature 字面量抓(走
|
||||||
|
`scan_code_file`);`rustc-wrapper` 抓不住(注入什么由程序决定)⇒ 一律判红,合法用途要 review 同意。
|
||||||
|
"""
|
||||||
|
problems = scan_code_file(path, root)
|
||||||
|
where = path.relative_to(root).as_posix()
|
||||||
|
for number, line in _lines(path):
|
||||||
|
if WRAPPER_KEY.match(strip_comment(line)):
|
||||||
|
problems.append(
|
||||||
|
f"::error file={where}:{number}::{where} 设了 rustc wrapper:wrapper 能注入任意 "
|
||||||
|
f"`--cfg`,静态扫描看不见它是否打开了 `{FEATURE}`(默认拒绝)"
|
||||||
|
)
|
||||||
|
return problems
|
||||||
|
|
||||||
|
|
||||||
def scan(root: Path) -> list[str]:
|
def scan(root: Path) -> list[str]:
|
||||||
"""扫整个仓库,返回所有问题(空 = 绿)。"""
|
"""扫整个仓库,返回所有问题(空 = 绿)。"""
|
||||||
problems: list[str] = []
|
problems: list[str] = []
|
||||||
@@ -287,6 +312,7 @@ def scan(root: Path) -> list[str]:
|
|||||||
(WORKFLOW_GLOBS, scan_workflow),
|
(WORKFLOW_GLOBS, scan_workflow),
|
||||||
(SCRIPT_GLOBS, scan_code_file),
|
(SCRIPT_GLOBS, scan_code_file),
|
||||||
(BUILD_ENTRY_GLOBS, scan_code_file),
|
(BUILD_ENTRY_GLOBS, scan_code_file),
|
||||||
|
(CONFIG_GLOBS, scan_cargo_config),
|
||||||
(MANIFEST_GLOBS, scan_manifest),
|
(MANIFEST_GLOBS, scan_manifest),
|
||||||
):
|
):
|
||||||
for path in sorted({p for pattern in patterns for p in root.glob(pattern)}):
|
for path in sorted({p for pattern in patterns for p in root.glob(pattern)}):
|
||||||
|
|||||||
@@ -189,6 +189,48 @@
|
|||||||
"files": {
|
"files": {
|
||||||
"Makefile": "build:\n\tcargo build --release --features update-test-source\n"
|
"Makefile": "build:\n\tcargo build --release --features update-test-source\n"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "P2-1 ⑥:`name:` 写在更深缩进的 env 子项里",
|
||||||
|
"expect": "red",
|
||||||
|
"files": {
|
||||||
|
".github/workflows/ci.yml": "jobs:\n update-e2e:\n steps:\n - name: update-e2e\n if: runner.os == 'Windows'\n run: |\n cargo test --release -p unlucky-cli --features update-test-source \\\n --test update_e2e_test --test update_e2e_tamper_test\n - run: cargo build --release -p unlucky-cli --features update-test-source\n env:\n T: --test update_e2e_test\n HINT: |\n - name: update-e2e\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "P2-1 ⑦:`name:` 写在 `run: |` 块标量里",
|
||||||
|
"expect": "red",
|
||||||
|
"files": {
|
||||||
|
".github/workflows/ci.yml": "jobs:\n update-e2e:\n steps:\n - name: update-e2e\n if: runner.os == 'Windows'\n run: |\n cargo test --release -p unlucky-cli --features update-test-source \\\n --test update_e2e_test --test update_e2e_tamper_test\n - run: |\n name: update-e2e\n cargo build --release --features update-test-source --test update_e2e_test\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "P2-1 ⑧:同一项里两个不同的 `name:`",
|
||||||
|
"expect": "red",
|
||||||
|
"files": {
|
||||||
|
".github/workflows/ci.yml": "jobs:\n update-e2e:\n steps:\n - name: update-e2e\n run: cargo build --release --features update-test-source --test update_e2e_test\n name: release-build\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "P2-2 ①:.cargo/config.toml 用 `--cfg feature=` 开该 feature",
|
||||||
|
"expect": "red",
|
||||||
|
"files": {
|
||||||
|
".cargo/config.toml": "[build]\nrustflags = [\"--cfg\", 'feature=\"update-test-source\"']\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "P2-2 ②:.cargo/config.toml 设 rustc-wrapper",
|
||||||
|
"expect": "red",
|
||||||
|
"files": {
|
||||||
|
".cargo/config.toml": "[build]\nrustc-wrapper = \"./tools/wrapper\"\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "误杀 ⑤:普通 .cargo/config.toml(无 feature / 无 wrapper)",
|
||||||
|
"expect": "green",
|
||||||
|
"files": {
|
||||||
|
".cargo/config.toml": "[build]\njobs = 4\n"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user