修复: 第五个落点 —— 判据写对了,却**没有任何人执行它**(check-file-modes.sh 空转)
pi `4fc16f66` 报的是 rc=2 那半个缝(M18)—— 我复核后**已经在 `e99a657` 修好了**,
而它的**父提交正是 pi 报信时读的 `6ee9902`**。又是同一形状的竞态。
但照 pi §四 那句「**每条分支的前提本身也要能被构造**」做审计时,
我发现**第五个落点,而且这一格的判据本身写得完全正确**:
`deploy/check-file-modes.sh` = 源文件权限政策的**唯一**判据。
它甚至专门修过自己那份 `[ -x ]` 恒真的 root 陷阱(`b7dc9e9`),改成直接读权限位 —— 修得对。
**但它从来没有被任何东西执行过。** 全仓 `grep -rn "check-file-modes"` 只有:
· `check-deploy-drift.mjs` 3 处**注释**(谈分工)
· `run-all.mjs:951` **注释**(讲 root 陷阱)
· `summary.py:77` **注释**,且**说法与实现不符**
· `summary.py:431` 一句 `print(...)` 的**文案**
⇒ 唯一一处非注释提及是一句 print 的文案。没有 install.sh / redeploy-* / hook / CI / cron 调它。
**而它当时正红着**:2 个文件是 600 + `jobs/` 缺属主 x 位。
★ 为什么这一类**只能靠判据守**(实测):
`git status` 看不见、`git diff` 看不见、`git ls-files -s` **只记 100644/100755**
(组/其他读位**不进版本库**)⇒ 把受跟踪文件 `chmod 600 ↔ 644` 两次 `git status` 都空。
套件也看不见(`jobs/` 缺 x 位时实测 `diag=none`、`ran=48`,全绿)⇒ **没有第二条通道**。
0600 在"跑的人恰好是属主"时不炸,换身份就是 EACCES,而那串报错**看起来像代码问题**。
修法:
· 接进 `deploy/install.sh`(check-shared-libs 之后),**走 `--check` 累积通道**
(照既有 npm_rc/CHECK_GATE_RC)。⚠️ 我第一版**直接调**,而门禁红时它 `exit 1`、
install.sh 是 `set -e` ⇒ 当场吞掉后面所有诊断:**实测**接线后 `构建 Gateway`
在日志里命中 **0 次**(接线前 2 次)—— 正是 install.sh:141-157 刚修过的同一个毛病,
我自己又造了一遍。改累积后回到 2 次,门禁报 `[FAIL] 权限政策没过(退出码 1)`。
· 修那两个文件:600 → 644 ⇒ 权限政策现在绿(rc=0)。
· 新增判据 `criteria-hygiene` 第 7 条:政策门禁必须被入口脚本在**可执行位置**调用。
★★ 变异验证时**我自己先假绿了一次**(单记):第一版用 `code()` 剥注释,而
`lib/read.mjs` 的 `stripComments()` 只认 `//` 与 `/* */` —— 那是 **JS** 的注释,
**`install.sh` 是 shell,注释是 `#`** ⇒ 对 shell 文件**原样返回** ⇒ 判据读到了
**我写在它上面那段解释里的** `` # `check-file-modes.sh` 红时 `exit 1` ``。
变异①(接线整段删掉)⇒ 第一版 **rc=0(假绿)**;自剥 shell `#` 后 **rc=1** ✓
变异②(只在 echo 里提一句)⇒ rc=1 ✓
变异③(gates 恒空 ∀x∈∅)⇒ rc=1(`只找到 0 个`)✓
(③ 第一次 python 锚点没匹配上、变异没施加,我重测并**先打印"变异已施加"**才算数。)
★ 射程如实标出:只管 `deploy/check-*.sh`(政策门禁族);**不管** `check-deploy-drift.mjs`
这类**按需手动工具**(自带 `--self-check`、文档写明"事后自查")——要求它进入口是**错的**。
它实测同样 0 处调用,但**本判据不覆盖它,也没修它**。判"有没有接线",不判"接得对不对"。
★ 通用纪律(CRITERIA.md §16.1.2):**判据自己也要能"被证明它真的在看代码"。**
若判据读的**语言**与 stripComments 实现的**语言**不一致(shell vs JS、Python vs JS),
那"剥注释"是**假的** ⇒ 判据消费散文,而**变异验证是唯一能戳破它的东西**。
全套 checks=516→**517** pass=512 fail=5 red=5 broken=0 verdict=red(5 条红都是别的会话的);
mutants=48 ran=48 skipped=0 diag=none baseline=7/7✓;5 个自检各 rc=0。
This commit is contained in:
@ -969,6 +969,91 @@ pi 报的第 1–3 例都修好之后,我顺着同一条线**审计全部 61
|
||||
> 同族的第三个自查问(pi 归纳):"守着它的判据,它自己的样本由谁守";
|
||||
> 这一条是它的**镜像**:"**被守的那个东西没挂上时,谁来说**"。
|
||||
|
||||
#### 16.1.2 ★★ 第五个落点:**判据写对了,却没有任何人执行它**(dsh 2026-09-19)
|
||||
|
||||
同一个形状的又一格,而且这一格的判据**本身写得完全正确**:
|
||||
|
||||
`deploy/check-file-modes.sh` = **源文件权限政策的唯一判据**
|
||||
("受跟踪文件不得比 0644 更严;可执行与否按 `git ls-files -s` 记的那位判")。
|
||||
它甚至专门修过**自己那份 `[ -x ]` 恒真的 root 陷阱**(`b7dc9e9`),改成直接读权限位 —— 修得对。
|
||||
|
||||
**但它从来没有被任何东西执行过。** 全仓 `grep -rn "check-file-modes"` 的结果:
|
||||
|
||||
```
|
||||
deploy/check-deploy-drift.mjs:147,149,442 ← 都在注释里谈"分工"
|
||||
client/electron/test/run-all.mjs:951 ← 注释(讲 root 陷阱)
|
||||
client/electron/test/mutants/summary.py:77 ← 注释,且**说法与实现不符**
|
||||
client/electron/test/mutants/summary.py:431 ← 一句 print(...) 的**文案**
|
||||
```
|
||||
|
||||
⇒ **唯一一处非注释提及是一句 `print` 的文案。** 没有任何 `install.sh` / `redeploy-*.sh` /
|
||||
hook / CI / cron 调用它。**而它当时正红着**:
|
||||
|
||||
```
|
||||
[FAIL] 权限过严:docs/DSH-0.1.5-MAIL-CHANNEL-ROOTCAUSE.md 是 600
|
||||
[FAIL] 权限过严:scripts/repair-legacy-spliced-ids.mjs 是 600
|
||||
[FAIL] 目录不可进入:client/electron/test/mutants/jobs(drw-r--r--,缺属主 x 位)
|
||||
```
|
||||
|
||||
★ **为什么这一类只能靠判据守、不能指望"顺手看见"**(实测):
|
||||
|
||||
| 手段 | 能否发现 600 vs 644 |
|
||||
|---|---|
|
||||
| `git status` | **不能**(两次都空) |
|
||||
| `git diff` | **不能** |
|
||||
| `git ls-files -s` | **只记 `100644`/`100755`** ⇒ 组/其他读位**不进版本库** |
|
||||
| 套件(`jobs/` 缺 x 位时) | **不能**(实测 `diag=none`、`ran=48`,全绿) |
|
||||
|
||||
⇒ 这一类**没有第二条通道**。0600 在"跑的人恰好是属主"时不炸,一旦换身份(`install.sh`
|
||||
自己会切身份判可写性;套件里还有**以 `nobody` 降权跑**的判据)就是 EACCES,
|
||||
而那串报错**看起来像代码问题**。
|
||||
|
||||
**`summary.py:77` 那句"那个只在部署时跑"是实现与说法不符**(实际从没跑过)——
|
||||
按本仓口径:**说法与实现不一致 ⇒ 以实现为准**,故以实现(从没跑)为准并把它接上线。
|
||||
|
||||
**修法**:接进 `deploy/install.sh`(`check-shared-libs.sh` 之后)。
|
||||
⚠️ **接线必须走 `--check` 的累积通道**(照既有 `npm_rc`/`CHECK_GATE_RC`):
|
||||
门禁红时它 `exit 1`,而 `install.sh` 是 `set -e` ⇒ 直接调会**在那里中止**、
|
||||
后面 `build-stamp`/钩子/收尾诊断**一行都不打**。
|
||||
**实测(我第一版就是直接调的)**:接线后 `构建 Gateway` 在日志里命中 **0 次**
|
||||
(接线前 2 次)—— 正是 `install.sh:141-157` 刚修过的同一个毛病,我自己又造了一遍。
|
||||
改成累积通道后 `构建 Gateway` 回到 2 次,门禁报 `[FAIL] 权限政策没过(退出码 1)`。
|
||||
|
||||
**新增判据**(`criteria-hygiene.test.mjs`,第 7 条):政策门禁(`deploy/check-*.sh`)
|
||||
必须至少被一个入口脚本在**可执行位置**调用。
|
||||
|
||||
★ **射程如实标出**:
|
||||
- 只管 `deploy/check-*.sh`(政策门禁那一族);**不管** `check-deploy-drift.mjs`
|
||||
这类**按需手动工具**(自带 `--self-check`、文档写明是"事后自查")——
|
||||
要求它也进入口是**错的**(把工具当门禁,然后逼人硬挂上去)。
|
||||
- 判"**有没有**接线",**不判"接得对不对"**(挂累积通道是另一条纪律)。
|
||||
- `check-deploy-drift.mjs` 同样不在任何入口被调用(实测 0 处),
|
||||
但它是**工具**不是门禁 ⇒ **本判据不覆盖它,也没修它**。
|
||||
|
||||
★★ **变异验证时我自己先假绿了一次(值得单记)**:
|
||||
|
||||
第一版用 `code()`(剥注释)读入口脚本 —— 而 `lib/read.mjs` 的 `stripComments()`
|
||||
只认 `//` 与 `/* */`,那是 **JS** 的注释,**`install.sh` 是 shell,注释是 `#`** ⇒
|
||||
对 shell 文件它**原样返回**。于是判据读到了**我写在它上面那段解释里的**
|
||||
`` # `check-file-modes.sh` 红时 `exit 1` `` ⇒ **变异验证①(把接线整段删掉)判据仍绿**。
|
||||
|
||||
| 变异 | 第一版(用 `code()`) | 修后(自剥 shell `#`) |
|
||||
|---|---|---|
|
||||
| ① 接线拆掉 | **rc=0(假绿)** | **rc=1** ✓ |
|
||||
| ② 只在 `echo` 里提一句 | — | **rc=1** ✓ |
|
||||
| ③ gates 恒空(∀x∈∅) | — | **rc=1**(`只找到 0 个`)✓ |
|
||||
|
||||
⇒ 修法:自己剥 shell 注释(行首/空白后的 `#`;**故意不碰 `${VAR#pat}`**),
|
||||
并抹掉 `echo`/`printf` 开头的散文行。**已知限制**:引号内的 `#` 会被当注释起点,
|
||||
方向是**假红**(漏掉接线),不是假绿 —— 假红当场看得见。
|
||||
|
||||
> **纪律(这一格的通用形式):判据自己也要能"被证明它真的在看代码"。**
|
||||
> 一条"在源码里找某个写法"的判据,若它**读的那种语言**和
|
||||
> `stripComments` **实现的那种语言**不一致(shell vs JS、Python vs JS、模板 vs 源码),
|
||||
> 那么"剥注释"是**假的** ⇒ 判据会**消费散文**,而**变异验证是唯一能戳破它的东西**。
|
||||
> 本仓 `stripComments` 自己那段注释早警告过"判据开始消费散文",
|
||||
> 但警告的是**字符串**,**没覆盖"另一种语言的注释"** —— 这一格是那个洞的实例。
|
||||
|
||||
### 16.2 ★★ **同一优先级写了两遍 ⇒ 两个顺序**:不变式只在**对角线**上被验过(pi + dsh 2026-09-18)
|
||||
|
||||
§16.1 讲"结论到不了决定颜色的那一格"。这一节是它的**镜像**:
|
||||
|
||||
@ -585,3 +585,116 @@ test('★ AGC 真身从未进过远端历史(泄露之后响的闹钟,不是
|
||||
+ ' 3) 查清是哪次 push 绕过了 `.githooks/pre-push`(`--no-verify`?没装的机器?)。\n'
|
||||
+ ` 命中:\n ${published.join('\n ')}`);
|
||||
});
|
||||
|
||||
/*
|
||||
* ★★ 政策门禁不许"写了但没人跑"(dsh 2026-09-19)。
|
||||
*
|
||||
* 形状(这是本仓第 5 次同一个形状,前 4 次见 `CRITERIA.md §16`):
|
||||
* `deploy/check-file-modes.sh` 是**源文件权限政策的唯一判据**,
|
||||
* 逻辑写得对(`b7dc9e9` 还专门修过它自己那份 `[ -x ]` 恒真的 root 陷阱),
|
||||
* **但全仓没有任何地方执行它** —— 唯一的非注释提及是 `mutants/summary.py`
|
||||
* 里一句 `print(...)` 的**文案**。而它当时**正红着**(2 个文件是 600、
|
||||
* `jobs/` 缺属主 x 位)。判据在,但走不到。
|
||||
*
|
||||
* 为什么这一类**只能靠判据守**、不能指望"顺手发现":
|
||||
* `git ls-files -s` 只记 **100644 / 100755**(只含可执行位),
|
||||
* **组/其他读位不进版本库** ⇒ 实测把受跟踪文件 `chmod 600 ↔ 644`,
|
||||
* `git status` 两次都空、`git diff` 也不动。没有任何"顺带"的通道。
|
||||
*
|
||||
* 判什么:**宣称是"政策/门禁"的 `deploy/check-*.sh`,必须至少被一个入口脚本
|
||||
* (`install.sh` / `redeploy-*.sh`)在**可执行位置**调用**。
|
||||
*
|
||||
* ★ 射程如实标出(别让它冒充比它更宽的东西):
|
||||
* · 只管 `deploy/check-*.sh`(**政策门禁那一族**),**不管** `check-deploy-drift.mjs`
|
||||
* 这类**按需手动工具** —— 后者自带 `--self-check`、文档里明确是"事后自查",
|
||||
* 要求它也进入口是**错的**(会把工具当门禁,然后逼人把它挂上去)。
|
||||
* · 判的是"**有没有**接线",**不判"接得对不对"**(挂在 `--check` 累积通道里
|
||||
* 而不是直接调,是另一条纪律,由 `install.sh` 自己的注释与 141-157 行守着)。
|
||||
* · 数的是**可执行位置**的出现,所以**注释里提到不算**(本仓纪律:
|
||||
* "注释说明禁令 ≠ 违反禁令")—— 用 `code()` 剥注释,与上一条同形。
|
||||
*/
|
||||
test('★ 政策门禁(deploy/check-*.sh)必须被入口脚本真正调用,不许"判据在但走不到"', () => {
|
||||
const DEPLOY = join(REPO_ROOT, 'deploy');
|
||||
const ENTRIES = ['install.sh', 'redeploy-gateway.sh', 'redeploy-plugin.sh'];
|
||||
|
||||
/*
|
||||
* ★★ 为什么**不能**直接用 `code()`(本判据第一版就是这么写的,**假绿了**):
|
||||
*
|
||||
* `lib/read.mjs` 的 `stripComments()` 只认 `//` 与 `/* *\/` —— 那是 **JS** 的注释。
|
||||
* `install.sh` 是 **shell**,它的注释是 `#`。于是 `code()` 对 shell 文件**原样返回**,
|
||||
* 包括我写在它上面那段解释里的一句
|
||||
* `# \`check-file-modes.sh\` 红时 \`exit 1\`,而本脚本是 \`set -e\``
|
||||
* ⇒ 变异验证(把接线整段删掉)**判据仍然绿** —— 它读到的是**我自己的解释文案**。
|
||||
* 实测:
|
||||
* 变异①(接线拆掉)⇒ 期望红,实际 **rc=0(绿)**
|
||||
* `code(install.sh).includes('check-file-modes.sh')` ⇒ **true**,命中的是 `#` 开头那行
|
||||
* 这正是本仓那条纪律的又一次现身("注释说明禁令 ≠ 违反禁令"),
|
||||
* 也是 `stripComments` 那段注释自己警告过的形状:"判据开始消费散文"。
|
||||
*
|
||||
* ⇒ 自己剥 shell 注释。**已知限制,方向如实标出**:
|
||||
* · 只处理"行首(可带空白)的 `#`"与"空白后的 ` #`";
|
||||
* `${VAR#pat}` 这种**没有空格**的写法因此**不会**被误当注释(正是想要的)。
|
||||
* · 引号内的 `#`(`echo "a # b"`)会被当成注释起点 ⇒ 该行后半被截掉。
|
||||
* 对本判据**方向是安全的**:截掉只会**漏掉**接线(假红),
|
||||
* 不会**凭空造出**接线(假绿)——而假红是会被人当场看见的那一侧。
|
||||
*/
|
||||
const stripShellComments = (src) => src.split('\n').map((line) => {
|
||||
const t = line.trimStart();
|
||||
if (t.startsWith('#')) return '';
|
||||
const m = line.match(/(^|\s)#/); // 行首或空白后的 #(避开 ${VAR#pat})
|
||||
return m ? line.slice(0, m.index) : line;
|
||||
}).join('\n');
|
||||
|
||||
/*
|
||||
* ★ 还要排掉"散文行":真接线长这样(命令位置)
|
||||
* "$REPO/deploy/check-shared-libs.sh"
|
||||
* bash "$REPO/deploy/check-sandbox.sh" ...
|
||||
* 而散文长这样
|
||||
* echo "药方:跑 deploy/check-file-modes.sh"
|
||||
* ⇒ 抹掉 `echo`/`printf` 开头的行。**不做字符串剥离** ——
|
||||
* 真接线里文件名**就在引号内**(`"$REPO/deploy/x.sh"`),
|
||||
* 剥字符串会把真接线一起剥掉(那是把判据弄瞎,不是弄准)。
|
||||
*/
|
||||
const entryCode = ENTRIES
|
||||
.map((e) => ({ e, src: stripShellComments(code(join(DEPLOY, e))) }))
|
||||
.filter(({ src }) => src.length > 0)
|
||||
.map(({ e, src }) => ({
|
||||
e,
|
||||
src: src.split('\n')
|
||||
.filter((l) => !/^\s*(echo|printf)\b/.test(l))
|
||||
.join('\n'),
|
||||
}));
|
||||
|
||||
/*
|
||||
* ★ 反空真护栏:本判据的结论是"每个门禁都有人调" ——
|
||||
* 如果 `deploy/check-*.sh` 一个都没列出来(路径写错、目录挪了),
|
||||
* `∀x∈∅` 会让它**看起来和真判据一样绿**。本仓纪律:"空真看起来和真判据一样绿"。
|
||||
*/
|
||||
const gates = readdirSync(DEPLOY)
|
||||
.filter((n) => /^check-.*\.sh$/.test(n))
|
||||
.sort();
|
||||
assert.ok(gates.length >= 3,
|
||||
`只找到 ${gates.length} 个 deploy/check-*.sh(期望 ≥3)—— 大概率是路径/目录变了,`
|
||||
+ '而不是"门禁真的变少了"。空真必须报红,不许静默通过。');
|
||||
|
||||
assert.ok(entryCode.length >= 2,
|
||||
`只读到 ${entryCode.length} 个入口脚本(期望 ≥2)—— 同上,反空真。`);
|
||||
|
||||
const unwired = [];
|
||||
for (const g of gates) {
|
||||
// 在**入口脚本的代码里**找这个文件名(含 `$REPO/deploy/` 前缀那种写法)
|
||||
const called = entryCode.some(({ src }) => src.includes(g));
|
||||
if (!called) unwired.push(g);
|
||||
}
|
||||
|
||||
assert.deepEqual(unwired, [],
|
||||
`这些政策门禁**写了却没有任何入口脚本调用**(判据在,但走不到):\n`
|
||||
+ ` ${unwired.join('\n ')}\n`
|
||||
+ ' 为什么这条要紧(实测形状):`deploy/check-file-modes.sh` 就这样空转了 ——\n'
|
||||
+ ' 它的权限政策判得**完全正确**,而它当时**正红着**(2 个文件 600 + `jobs/` 缺 x),\n'
|
||||
+ ' 全仓唯一的非注释提及是另一个文件里一句 `print(...)` 的文案。\n'
|
||||
+ ' ⇒ 药方:在 `deploy/install.sh` 里调用它。\n'
|
||||
+ ' ⚠️ 挂在 `--check` 下要**走累积通道**(照 `npm_rc`/`CHECK_GATE_RC` 的既有做法),\n'
|
||||
+ ' **不能直接调** —— 门禁红时它 `exit 1`,而 `install.sh` 是 `set -e`,\n'
|
||||
+ ' 会在那里中止、后面所有诊断一行都不打(`install.sh` 141-157 行刚修过同一个毛病)。');
|
||||
});
|
||||
|
||||
@ -112,7 +112,7 @@ const SUITE = [
|
||||
['test/debt-visibility.test.mjs', [], 1],
|
||||
['test/commit-hygiene.test.mjs', ['--experimental-strip-types', '--no-warnings'], 4],
|
||||
// 判据目录自身的卫生:读文本必须走 test/lib/read.mjs 的具名入口
|
||||
['test/criteria-hygiene.test.mjs', [], 6],
|
||||
['test/criteria-hygiene.test.mjs', [], 7],
|
||||
// 用户管理页(P4c 同批):动作↔服务端调用同名 / 门禁只认严格 admin /
|
||||
// 启停只发 status / 「受限」徽标口径 / 页面零写死色值 / 接线(纯逻辑真被调用)
|
||||
['test/harmony-admin.test.mjs', ['--experimental-strip-types', '--no-warnings'], 30],
|
||||
|
||||
@ -280,6 +280,45 @@ echo "==> 校验插件共用模块同源"
|
||||
# 一侧改了另一侧没改,几个平台的行为就会悄悄分叉。
|
||||
"$REPO/deploy/check-shared-libs.sh"
|
||||
|
||||
# ★★ 源文件权限政策(dsh 2026-09-19 接线)。
|
||||
#
|
||||
# 为什么单独一条、为什么**必须挂在这里**:这一类**只有它一个判据**,而且
|
||||
# `git` 根本看不见 —— `git ls-files -s` 只记 **100644 / 100755**(可执行位),
|
||||
# **组/其他读位不进版本库**。实测:把受跟踪文件 `chmod 600 ↔ 644`,
|
||||
# `git status` **两次都空**、`git diff` 也不动 ⇒ 没有任何"顺带"的通道会发现它。
|
||||
#
|
||||
# 后果不是"本地难看":这些是**源码**,会被构建/测试/部署**以不同身份**读
|
||||
# (`install.sh` 自己会切身份做可写性判定;`client/electron/test` 里还有
|
||||
# **以 nobody 降权跑**的判据)。0600 在"跑的人恰好是属主"时不炸,换个身份就是
|
||||
# EACCES —— 而那串报错**看起来像代码问题**。
|
||||
#
|
||||
# ⚠️ 这条判据**写出来很久了却从没被任何东西调用过**(`grep -rn` 全仓:唯一的
|
||||
# 非注释提及是 `mutants/summary.py` 里一句 `print(...)` 的文案),
|
||||
# 而它**当时正红着**(3 条:2 个 600 的文件 + `jobs/` 缺 x 位)——
|
||||
# 正是本仓反复消的那个形状:"**判据在,但走不到**"。
|
||||
# 它自己那份 `[ -x ]` 恒真的 root 陷阱也修过一版(`b7dc9e9`),修得对,
|
||||
# 但**没有任何人会跑到那一行**,所以修与不修没有可观察差别。
|
||||
#
|
||||
# ⚠️⚠️ 接线时必须走 `--check` 的**累积**通道(`CHECK_GATE_RC`),不能直接调:
|
||||
# `check-file-modes.sh` 红时 `exit 1`,而本脚本是 `set -e`
|
||||
# ⇒ 它会在**这里**中止,后面 `build-stamp`/钩子/`--check` 的收尾诊断**一行都不打**。
|
||||
# 我第一版就是直接调的,**实测**:接线后 `构建 Gateway`/`启用并启动` 等在日志里
|
||||
# **命中 0 次**(接线前 `构建 Gateway` 命中 2 次)—— 那正是本文件 141-157 行
|
||||
# 刚修过的同一个毛病("第一道红吃掉后面所有诊断"),我自己又造了一遍。
|
||||
# ⇒ 照 `npm_rc` 的既有做法:真装路径仍 `set -e` 立刻中止(装机器时不该带着红往下走),
|
||||
# 干跑路径**记下来、跑完、最后 `exit "${CHECK_GATE_RC:-0}"`**。
|
||||
if [[ $CHECK_ONLY -eq 1 ]]; then
|
||||
_fm_rc=0
|
||||
"$REPO/deploy/check-file-modes.sh" || _fm_rc=$?
|
||||
if [[ $_fm_rc -ne 0 ]]; then
|
||||
echo " [FAIL] 权限政策没过(退出码 ${_fm_rc})—— 上面标了 [FAIL] 的那几处就是" >&2
|
||||
# 只记**第一个**失败码(既有做法):`CHECK_GATE_RC` 是"干跑退出码"的单一来源
|
||||
[[ -n "${CHECK_GATE_RC:-}" ]] || CHECK_GATE_RC="$_fm_rc"
|
||||
fi
|
||||
else
|
||||
"$REPO/deploy/check-file-modes.sh"
|
||||
fi
|
||||
|
||||
# 插件的纯函数测试(自动转发去重等)。
|
||||
# 插件不参与构建产物,但它的逆行为会直接变成用户收件箱里的重复邮件,
|
||||
# 因此也纳入部署前的门禁。zod 已在 node_modules 里就不重装。
|
||||
|
||||
Reference in New Issue
Block a user