4322deb23a
總管自己驗這一格量到 exit=0,我量到 exit=1。**兩個都是真的**,
而分歧本身就是最後一塊拼圖:
**GNU 的 `-f` 是 `--file-system`,布林旗標、不接格式字串**
⇒ `%m` 不是格式,它被當成**另一個檔名運算元**
⇒ 離開碼取決於「cwd 裡有沒有一個叫 `%m` 的檔」:
A. 沒有(一般情況) → exit 1 ⇒ `||` 會跑 ⇒ 正確的秒數接在垃圾後面
B. 剛好有 → exit 0 ⇒ `||` 不會跑 ⇒ 整包連一個數字都沒有
兩種情況閘的結果一樣:MT 都不是純數字、戳記都作廢。(實測 GNU coreutils 9.4,兩種都重現過)
🔴 教訓比原本寫的更尖銳:舊寫法的 `||` fallback 救不了,
**不是因為它沒跑,而是因為「跑不跑」根本不由這支腳本決定**——
它由「cwd 裡有沒有某個檔名」決定。
一個行為取決於 cwd 有沒有某個檔的判斷式,不管跑不跑都是壞的。
⇒ 所以修法不能只是「把順序反過來」,**每一步都要驗它是不是純數字**
(lib/mtime.sh 本來就是這樣寫的,現在把理由寫進去了)。
gate-ok 測試補第 ⑰ 條守這一層:cwd 裡有一個叫 `%m` 的檔時,file_mtime 仍要回純數字。
16 → 17 條,TESTING.md 的 A16 一併更新。
58 lines
3.5 KiB
Bash
58 lines
3.5 KiB
Bash
# hooks/lib/mtime.sh — 「這個檔幾點被動的」,跨 macOS/Linux 都問得出來。
|
||
# 不是獨立掛的閘(沒進 hooks.json),給那幾支用 /tmp 戳記的閘 `source` 用。
|
||
#
|
||
# ══ 為什麼要有這支(inkstone/ISEP#90 ④,2026-08-28 實查)═══════════════
|
||
#
|
||
# 三支閘(prod-write-guard、main-and-prod-push-guard、stage-before-prod-guard)
|
||
# 的戳記檢查都寫成這一行:
|
||
#
|
||
# MT=$(stat -f %m "$STAMP" 2>/dev/null || stat -c %Y "$STAMP" 2>/dev/null || echo 0)
|
||
#
|
||
# 在 macOS(BSD stat)上它是對的:`-f %m` 就是 mtime。
|
||
# 在 **Linux(GNU coreutils)上 `-f` 是「顯示檔案系統資訊」**,而且它
|
||
# **一邊回非零、一邊往 stdout 吐一整段檔案系統的區塊**:
|
||
#
|
||
# $ stat -f %m /tmp/.probe
|
||
# stat: cannot read file system information for '%m': No such file or directory ← stderr
|
||
# File: "/tmp/.probe" ← stdout
|
||
# ID: 0 Namelen: 255 Type: ext2/ext3
|
||
# …
|
||
#
|
||
# ⇒ `2>/dev/null` 把錯誤訊息吃掉、`||` 接著跑 `stat -c %Y` 把正確的秒數**接在那堆垃圾後面**
|
||
# ⇒ `MT` 變成「一段多行文字+一個數字」
|
||
# ⇒ 閘下一行的 `case "$NOW$MT" in *[!0-9]*) return 1` 一定命中
|
||
# ⇒ **在 Linux 上,那三支閘的戳記永遠不會被接受。**
|
||
#
|
||
# 🔴 這件事的後果不是「少一個便利功能」:
|
||
# 那三支閘都是「擋下來,但**總管看過就可以放行**」的設計。
|
||
# 放行的那道門在雲端(Linux)打不開 ⇒ **它們在雲端等於純擋**,
|
||
# 總管照著閘自己印的指示做,做幾次都打不開,而閘不會告訴他門是壞的。
|
||
# inkstone/InkStoneCo#99 記的「連續四次蓋不出戳記」就是這個形狀。
|
||
#
|
||
# ── 再往下一層:`-f` 根本不吃格式參數,所以連離開碼都不可靠 ────────────
|
||
#
|
||
# 2026-08-28 兩個人量這一格,一個量到 `exit=0`、一個量到 `exit=1`。**兩個都是真的**,
|
||
# 而分歧本身就是最後一塊拼圖:**GNU 的 `-f` 是 `--file-system`,布林旗標、不接格式字串**
|
||
# ⇒ `%m` 被當成**另一個檔名運算元** ⇒ 離開碼取決於「cwd 裡有沒有一個叫 `%m` 的檔」:
|
||
#
|
||
# A. 沒有(一般情況) → exit 1 ⇒ `||` **會**跑 ⇒ 正確的秒數接在垃圾後面
|
||
# B. 剛好有 → exit 0 ⇒ `||` **不會**跑 ⇒ 整包連一個數字都沒有
|
||
#
|
||
# 兩種情況下閘的結果一樣:`MT` 都不是純數字,戳記都作廢。(實測 GNU coreutils 9.4)
|
||
#
|
||
# 🔴 **這才是該記住的教訓**:舊寫法的 `||` fallback 救不了,
|
||
# **不是因為它沒跑,而是因為「跑不跑」根本不由這支腳本決定**
|
||
# ——它由「cwd 裡有沒有某個檔名」決定。
|
||
# 一個**行為取決於 cwd 裡有沒有某個檔**的判斷式,不管跑不跑都是壞的。
|
||
#
|
||
# 🔴 所以修法不是「把順序反過來」而已:改成先 `-c %Y`(GNU)再 `-f %m`(BSD),
|
||
# 而且**每一步都驗它是不是純數字**——因為這個 bug 的成因正是
|
||
# 「命令失敗了卻還是印了東西出來」,只看離開碼會再被騙一次。
|
||
file_mtime() {
|
||
_fm=$(stat -c %Y "$1" 2>/dev/null || true)
|
||
case "${_fm:-}" in ''|*[!0-9]*) _fm=$(stat -f %m "$1" 2>/dev/null || true) ;; esac
|
||
case "${_fm:-}" in ''|*[!0-9]*) _fm=$(python3 -c 'import os,sys; print(int(os.path.getmtime(sys.argv[1])))' "$1" 2>/dev/null || true) ;; esac
|
||
case "${_fm:-}" in ''|*[!0-9]*) _fm=0 ;; esac
|
||
printf '%s' "$_fm"
|
||
}
|