feat(tools): 新增 git 還原閘門腳本,建置測試加上唯讀偵測模式
What:新增 `tools/git-guard.sh`,把工作區乾淨檢查與還原序列從技能內文搬進腳本。`tools/build-test.sh` 加上 `--detect` 唯讀模式,只偵測建置與測試指令,不執行。 Why:還原會執行 `git clean -fd`。這個指令刪掉未追蹤的檔案,沒有 reflog 可救,也沒有任何救回的路徑。前提原本只寫在技能內文,讀的人漏掉一行,就可能對著一個根本不該還原的目錄動手,把整個工作區的未追蹤檔案清光。前提要擋得住,就得寫成程式碼。另一件事是「推不出建置與測試指令」,原本要等所有版本來源檔案都改寫完才發現,使用者又答不出指令時,前面全部作廢,還得走一次還原。 How:`revert` 在跑第一個破壞性指令之前擋五道——目錄存在、git 指令存在、確實是 git 工作樹(不是裸存取庫)、呼叫端明確帶 `--confirm-destructive`、目標不是檔案系統根目錄。任何一道不過就直接結束,工作區一個位元組都不動。`check` 的乾淨判定看整個存取庫,`revert` 只作用在專案目錄以下,判定從嚴、動手從窄。`clean` 用 `-fd` 不用 `-fdx`,被 gitignore 的安裝產物要留著。`--detect` 只做偵測與前置檢查,一個檔案都不寫,結束碼與路由跟執行模式完全一致。 Who:套件批次更新的前置檢查與還原路線。
This commit is contained in:
+34
-5
@@ -1,25 +1,37 @@
|
||||
#!/usr/bin/env sh
|
||||
# build-test.sh — 偵測並執行專案的建置與測試,先建置再測試。
|
||||
# 用法:build-test.sh <ecosystem> <project-dir>
|
||||
# 用法:
|
||||
# build-test.sh <ecosystem> <project-dir> 偵測後實際執行
|
||||
# build-test.sh --detect <ecosystem> <project-dir> 只偵測,唯讀,不執行任何建置或測試
|
||||
# ecosystem:nodejs | python | dotnet
|
||||
# 偵測方式:
|
||||
# nodejs → package.json 的 scripts.build 與 scripts.test(npm init 產生的佔位 test 視為沒有)
|
||||
# python → pytest 設定(pytest.ini、setup.cfg、tox.ini、pyproject.toml)或 tests 目錄與 test_*.py
|
||||
# dotnet → *.sln 或 *.csproj,建置與測試都用 dotnet
|
||||
# 輸出:底層指令的原始輸出。
|
||||
# 為什麼要有 --detect:推不出指令(5)本來要等所有版本都改寫完才發現,使用者又答不出指令時,
|
||||
# 前面全數作廢還要走還原。--detect 讓呼叫方在動任何檔案之前先問出結果,工作區完全沒被動過。
|
||||
# --detect 只做偵測與前置檢查,一個檔案都不寫。
|
||||
# 輸出:底層指令的原始輸出;--detect 模式輸出偵測到的指令名稱,一行一個(nodejs 是 build、test,
|
||||
# python 是 pytest,dotnet 是 dotnet build、dotnet test)。
|
||||
# 結束碼(慣例見 README「工具」章的結束碼總表):
|
||||
# 0 建置與測試都通過
|
||||
# 0 建置與測試都通過;--detect 模式是推得出指令
|
||||
# 1 參數個數不對
|
||||
# 2 ecosystem 不認識
|
||||
# 4 需要的指令不存在(npm、python3、pytest、dotnet)
|
||||
# 5 推不出任何建置或測試指令——呼叫方要改問使用者
|
||||
# 6 找不到專案目錄——呼叫方停手
|
||||
# 其他 建置或測試失敗,帶回失敗指令的結束碼
|
||||
# 其他 建置或測試失敗,帶回失敗指令的結束碼;--detect 模式不會回這一類
|
||||
# 本工具不用 3:3 保留給「該 ecosystem 沒有來源檔案」,這支改用 5 表達推不出指令。
|
||||
set -u
|
||||
|
||||
DETECT=0
|
||||
if [ "${1:-}" = "--detect" ]; then
|
||||
DETECT=1
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ "$#" -ne 2 ]; then
|
||||
echo "用法:build-test.sh <ecosystem> <project-dir>" >&2
|
||||
echo "用法:build-test.sh [--detect] <ecosystem> <project-dir>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -49,6 +61,10 @@ EOF
|
||||
)
|
||||
[ -n "$scripts" ] || { echo "no build or test command inferred for $DIR" >&2; exit "$NO_COMMAND"; }
|
||||
command -v npm >/dev/null 2>&1 || { echo "npm not found" >&2; exit 4; }
|
||||
if [ "$DETECT" -eq 1 ]; then
|
||||
printf '%s\n' "$scripts"
|
||||
exit 0
|
||||
fi
|
||||
for s in $scripts; do
|
||||
( cd "$DIR" && npm run --if-present "$s" ) || exit $?
|
||||
done
|
||||
@@ -67,9 +83,17 @@ EOF
|
||||
fi
|
||||
[ "$has_tests" -eq 1 ] || { echo "no build or test command inferred for $DIR" >&2; exit "$NO_COMMAND"; }
|
||||
if command -v pytest >/dev/null 2>&1; then
|
||||
if [ "$DETECT" -eq 1 ]; then
|
||||
echo "pytest"
|
||||
exit 0
|
||||
fi
|
||||
( cd "$DIR" && pytest )
|
||||
exit $?
|
||||
elif command -v python3 >/dev/null 2>&1 && python3 -m pytest --version >/dev/null 2>&1; then
|
||||
if [ "$DETECT" -eq 1 ]; then
|
||||
echo "python3 -m pytest"
|
||||
exit 0
|
||||
fi
|
||||
( cd "$DIR" && python3 -m pytest )
|
||||
exit $?
|
||||
else
|
||||
@@ -82,6 +106,11 @@ EOF
|
||||
projs=$(find "$DIR" -maxdepth 3 \( -name '*.sln' -o -name '*.csproj' \) 2>/dev/null)
|
||||
[ -n "$projs" ] || { echo "no build or test command inferred for $DIR" >&2; exit "$NO_COMMAND"; }
|
||||
command -v dotnet >/dev/null 2>&1 || { echo "dotnet not found" >&2; exit 4; }
|
||||
if [ "$DETECT" -eq 1 ]; then
|
||||
echo "dotnet build"
|
||||
echo "dotnet test"
|
||||
exit 0
|
||||
fi
|
||||
( cd "$DIR" && dotnet build ) || exit $?
|
||||
( cd "$DIR" && dotnet test ) || exit $?
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user