950c3e1919
現象(實測,不是推論):跑完 main-and-prod-push-guard 那三支測試之後
$ git status --short
M hooks/lib/__pycache__/strip_heredoc.cpython-314.pyc
M pending-main-push/unnamed--ISEP.md
?? pending-main-push/unnamed--A.md
成因:這支閘擋下推 main 的同時,會把那次請求寫成
`<hooks 的上一層>/pending-main-push/<誰>--<repo>.md`,而三支測試的測資本來
就全是推 main。於是每跑一次測試,工作區就多/改幾筆**偽造的待裁決**。
兩個後果,後者比較貴:
① `git add -A` 很容易把它們帶進 commit(08-27 那次真的帶進去了,事後才拔掉)
② 總管的迴圈讀那個目錄,讀到的每一筆都該是真的在等他裁——
測試每跑一次就偽造一筆 ⇒ **下一筆真的請求會混在雜訊裡**。
這跟「永遠在響的警報」是同一個病。
改法(產品程式碼一行都沒動):
- 新增 hooks/tests/lib/hook-sandbox.sh:把整個 hooks/ 複製到暫存區再跑複本。
閘算 pending 目錄的位置靠的是 `$0` 的上一層 ⇒ 請求寫進暫存區。
**刻意不在閘上開一個「寫去哪」的環境變數**——那種開關同時是一條把紀錄關掉的路。
- 三支測試改測沙盒複本,並各補兩條斷言:
① repo 的 pending-main-push 一個位元都沒動
② 沙盒裡**真的有**留下請求(只驗 ① 的話,把留紀錄的功能整個關掉也會綠)
- pending-main-push/ 不再進版控(它是本機狀態不是原始碼),只留一份 README 說明規約;
既有的兩筆與那顆被追蹤的 .pyc 一併 `git rm --cached`,檔案留在硬碟上不刪。
實測:
- 三支各自 10/10、19/19、13/13(原本 8/17/11,各多兩條新斷言)
- 把 `H` 改回真跡重跑 ⇒ 新斷言兩條都紅(11/13)+工作區又髒
⇒ 這兩條斷言真的抓得到它要抓的東西
- 全套 16 支測試檔跑完 0 失敗,`git status` 沒有多出任何一行
- `claude plugin validate .` ✔ Validation passed
沒有升版:這次只動測試與版控範圍,沒有任何閘的行為改變,
不需要靠新版本號送到任何人手上;請跟著總管下一個 release 一起出去。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
2.8 KiB
Bash
Executable File
53 lines
2.8 KiB
Bash
Executable File
#!/bin/bash
|
||
# main-and-prod-push-guard 的測試(inkstone/InkStoneCo#23、#56)
|
||
# 判準:真的推 main/master 要擋;只是提到(heredoc body、同一行引號內的散文、
|
||
# 開頭包一層讀取指令)都不准擋;推自己的分支、含子字串但目標不是 main/master
|
||
# 的分支(如 fix/custom-domain-setup)也不准擋。
|
||
cd "$(dirname "$0")/.." || exit 1
|
||
|
||
# 🔴 測沙盒裡的複本,不測真跡(inkstone/ISEP#59 comment 4779 第 ① 條):
|
||
# 這支閘擋下推 main 的同時會把請求寫進 `<repo>/pending-main-push/`,那份檔案的
|
||
# 語意是「有一筆推 main 正在等總管裁」。這裡的測資本來就都是推 main
|
||
# ⇒ 直接測真跡的話,**每跑一次測試就偽造一筆待裁決**(實測:`unnamed--ISEP.md`
|
||
# 每跑一次被覆寫一次,而它當時還是被追蹤的 ⇒ `git status` 永遠是髒的)。
|
||
# cwd 仍然留在 repo 根——戳記比對問的是「人站在哪個 repo」,那件事沒有變。
|
||
. hooks/tests/lib/hook-sandbox.sh
|
||
hook_sandbox "$PWD/hooks/main-and-prod-push-guard.sh" || { echo "❌ 沙盒建不起來"; exit 1; }
|
||
trap 'hook_sandbox_cleanup' EXIT
|
||
HOSTSUM_BEFORE=$(hook_sandbox_hostsum)
|
||
H=$HOOK_SANDBOX_HOOK
|
||
|
||
PASS=0; FAIL=0
|
||
run(){ # $1=want $2=cmd
|
||
printf '%s' "{\"tool_name\":\"Bash\",\"tool_input\":{\"command\":$(python3 -c 'import json,sys;print(json.dumps(sys.argv[1]))' "$2")}}" \
|
||
| bash "$H" >/dev/null 2>&1
|
||
got=$?
|
||
if [ "$got" = "$1" ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
|
||
printf 'want=%s got=%s %.72s\n' "$1" "$got" "$2"
|
||
}
|
||
|
||
echo "── 該擋:真的在推 main/master,且沒有戳記 ──"
|
||
run 2 'git push gitea HEAD:main'
|
||
run 2 'git push origin master'
|
||
run 2 'echo start && git push gitea HEAD:main'
|
||
run 2 'ls && git push gitea HEAD:main'
|
||
|
||
echo "── 不該擋:只是提到、heredoc body、目標不是 main/master ──"
|
||
run 0 'git push gitea HEAD:fix/custom-domain-setup'
|
||
run 0 'git push gitea HEAD:fix/mainline-cleanup-later'
|
||
run 0 'git commit -m "docs: explain that the old gate would mis-flag push mentions"'
|
||
run 0 "$(printf 'cat > docs/TESTING.md <<%sEOF%s\nexample: git push https://github.com/example/example.git HEAD:main\nEOF\n' "'" "'")"
|
||
run 0 'python3 -c "requests.post(url, json={\"body\": \"this quotes: the gate cannot see a real git push done via a node subprocess, but it will block git push origin main mentioned as prose\"})"'
|
||
run 0 'grep -n "git push" hooks/main-and-prod-push-guard.sh'
|
||
|
||
echo "── 真違規不能因為這次改動而漏擋:戳記過期/戳記綁錯 repo 一樣要擋 ──"
|
||
run 2 'git push gitea HEAD:main' # 沒有任何戳記檔時
|
||
|
||
echo "── 測試自己不准弄髒工作區(inkstone/ISEP#59)──"
|
||
hook_sandbox_assert "$HOSTSUM_BEFORE"
|
||
PASS=$((PASS+HS_PASS)); FAIL=$((FAIL+HS_FAIL))
|
||
|
||
echo
|
||
echo "$PASS/$((PASS+FAIL)) 通過"
|
||
[ "$FAIL" -eq 0 ]
|