出貨鏈三站上 main:release-ship 前置①改看 tag 的樹、release-check 可指定版本、信標開場報「打了 tag 沒 release」(inkstone/ISEP#67)

- hooks/lib/release_chain.py:問 Gitea 的判準一份三處用(匿名讀,D20)
- scripts/release-ship:前置①比「要建的那個 tag 的樹」裡的 plugin.json,不比工作樹/最新 tag
  (總管 09-07 在 v0.23.0 被擋的情境現在放行);note 從 Gitea compare 生,從 plugin 快取跑也行;
  覆核改跑 release-check <那個 tag>
- scripts/release-check [vX.Y.Z]:指定版本;v0.19/0.20/0.21 依裁決標「不補」不再黃字;補法印正本絕對路徑
- hooks/lib/beacon_report.py ④:最新 tag 沒 release/main 定版沒打 tag ⇒ 一行缺站+補法;都在安靜;
  只快取「都在」6 小時
- 測試:test-release-ship 14、test-release-check 14、信標 26→33;docs:TESTING A37/A38、盤點表、README 60 支腳本、
  白名單加 release-ship/release-check 形狀、mistakes 一條
- 沒改 plugin.json 版本號(待總管定版)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0178ef1fGw3XeZtpN7LaZrm4
This commit is contained in:
claude-code
2026-09-07 05:52:41 +00:00
parent 1fa45cd539
commit fef35ed03a
12 changed files with 717 additions and 332 deletions
+52 -18
View File
@@ -3,15 +3,22 @@
#
# 兩個方向都要有證據:
# A 群「該擋」(exit 2)——前置條件沒滿足就不給建 release,且說得出是哪一個前置
# ① 版本不一致 ② Gitea 上沒那個 tag ③ 版本寫法不對
# B 群「該放行」(exit 0)——前置都在就建;release 已存在則冪等不重複建;--dry-run 不 POST
# ① 那個 tag 的樹裡 plugin.json 跟 tag 號碼對不上 ② Gitea 上沒那個 tag ③ 版本寫法不對
# B 群「該放行」(exit 0)——前置都在就建;release 已存在則冪等不重複建;--dry-run 不 POST
# 🔴 **不是最新的那個 tag 也建得了**(總管 09-07 在 v0.23.0 被擋的那個情境)
#
# 🔴 全程離線(RELEASE_SHIP_FIXTURE RELEASE_SHIP_SINK),不打 Gitea、不建任何真 release。
# fixture 的形狀見 hooks/lib/release_chain.py `Chain` 的註解。
set -u
TOOL="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/release-ship}"
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0; N=0
# 🔴 工作樹故意擺一個跟任何 tag 都對不上的版本:前置①要是偷看工作樹,B 群會整組紅
WT="$TMP/worktree"; mkdir -p "$WT/.claude-plugin"
printf '{"name":"isep","version":"9.9.9"}\n' > "$WT/.claude-plugin/plugin.json"
export CLAUDE_PROJECT_DIR="$WT"
# t <期望exit> <說明> <必須出現的字串> <fixture檔> [額外參數…]
t() {
want="$1"; desc="$2"; must="$3"; fx="$4"; shift 4
@@ -29,25 +36,36 @@ t() {
printf '%s\n' "$out" | sed -n '1,14p' | sed 's/^/ /'; FAIL=$((FAIL+1))
fi
}
# not <說明> <不該出現的字串> <fixture檔> [額外參數…]
not() {
desc="$1"; bad="$2"; fx="$3"; shift 3
N=$((N+1))
rm -f "$TMP/sink.json" "$TMP/sink.json.checkfx.json"
out=$(RELEASE_SHIP_FIXTURE="$TMP/$fx" RELEASE_SHIP_SINK="$TMP/sink.json" \
python3 "$TOOL" "$@" 2>&1)
if printf '%s' "$out" | grep -qF "$bad"; then
printf ' ❌ %s —— 不該出現卻出現了:%s\n' "$desc" "$bad"; FAIL=$((FAIL+1))
else printf ' ✅ %s\n' "$desc"; PASS=$((PASS+1)); fi
}
echo "── A 群:該擋(前置條件沒滿足)──"
# A1版本一致性沒過
# A1tag 的樹裡 plugin.json 跟 tag 號碼對不上(這種 tag 本來就不該存在)
cat > "$TMP/a1.json" <<'JSON'
{"consistency_ok":false,"consistency_msg":"❌ plugin.json=0.21.0 但 tag=v0.22.0",
"tags":["v0.22.0"],"releases":[]}
{"tags":["v0.22.0"],"manifest":{"v0.22.0":"0.21.0"},"releases":[]}
JSON
t 2 "版本不一致 → 擋,指名前置①" "前置①" a1.json v0.22.0
t 2 "tag 的樹裡 plugin.json 是 0.21.0、tag 是 v0.22.0 → 擋,指名前置①" "前置①" a1.json v0.22.0
t 2 "前置①被擋時講得出修法(刪 tag、重新定版)" "重新定版" a1.json v0.22.0
# A2Gitea 上沒有這個 tag
cat > "$TMP/a2.json" <<'JSON'
{"consistency_ok":true,"tags":["v0.18.0"],"releases":["v0.18.0"]}
{"tags":["v0.18.0"],"manifest":{"v0.18.0":"0.18.0"},"releases":["v0.18.0"]}
JSON
t 2 "Gitea 沒有該 tag → 擋,指名前置②+叫人先推 tag" "git push origin v0.22.0" a2.json v0.22.0
# A3:版本寫法不對
cat > "$TMP/a3.json" <<'JSON'
{"consistency_ok":true,"tags":[],"releases":[]}
{"tags":[],"releases":[]}
JSON
t 2 "版本寫法不對 → 擋" "版本寫法" a3.json "0.22"
@@ -55,26 +73,42 @@ echo "── B 群:該放行 ──"
# B1:前置都在、release 還沒建 → 建(fixture sink),然後覆核通過
cat > "$TMP/b1.json" <<'JSON'
{"consistency_ok":true,
"current":"0.22.0","dingban":["0.22.0","0.18.0"],"loaded":"0.22.0",
"tags":["v0.22.0","v0.18.0"],"releases":["v0.18.0"]}
{"current":"0.22.0","dingban":["0.22.0","0.18.0"],"loaded":"0.22.0",
"tags":["v0.22.0","v0.18.0"],"manifest":{"v0.22.0":"0.22.0","v0.18.0":"0.18.0"},
"releases":["v0.18.0"],
"commits":{"v0.18.0...v0.22.0":["定版 v0.22.0ship-check 送得到)","修一支閘(inkstone/ISEP#1"]}}
JSON
t 0 "前置齊全、release 缺 → 建起來" "已建立 release「v0.22.0」" b1.json v0.22.0 --notes-file "$TMP/b1.json"
t 0 "建完回頭跑 release-check 覆核(問收貨端)" "覆核" b1.json v0.22.0 --notes-file "$TMP/b1.json"
t 0 "前置齊全、release 缺 → 建起來" "已建立 release「v0.22.0 — ship-check 送得到」" b1.json v0.22.0
t 0 "建完回頭跑 release-check 覆核(問收貨端)" "覆核release-check v0.22.0" b1.json v0.22.0
t 0 "note 自動從 Gitea 的 commit 清單生成(不靠本機 git log" "commit 清單自動生成" b1.json v0.22.0
# B2:release 已存在 → 冪等,不重複建
# B2:release 已存在 → 冪等,不重複建、不動它
cat > "$TMP/b2.json" <<'JSON'
{"consistency_ok":true,
"current":"0.18.0","dingban":["0.18.0"],"loaded":"0.18.0",
"tags":["v0.18.0"],"releases":["v0.18.0"]}
{"current":"0.18.0","dingban":["0.18.0"],"loaded":"0.18.0",
"tags":["v0.18.0"],"manifest":{"v0.18.0":"0.18.0"},"releases":["v0.18.0"]}
JSON
t 0 "release 已存在 → 冪等放行、不重複建" "本來就在了" b2.json v0.18.0 --notes-file "$TMP/b2.json"
not "release 已存在時不准說「已建立」(09-05 那句假綠)" "已建立 release" b2.json v0.18.0
# B3--dry-run 不 POST
cat > "$TMP/b3.json" <<'JSON'
{"consistency_ok":true,"tags":["v0.22.0"],"releases":[]}
{"tags":["v0.22.0"],"manifest":{"v0.22.0":"0.22.0"},"releases":[]}
JSON
t 0 "--dry-run 全走一遍但不 POST" "不 POST" b3.json v0.22.0 --dry-run
not "--dry-run 不寫 sink(真的沒建)" "fixture-created" b3.json v0.22.0 --dry-run
# B4:🔴 不是最新的 tag 也建得了(總管 09-07:在 v0.23.0 的 checkout 跑被擋,訊息說最新是 v0.24.0)
cat > "$TMP/b4.json" <<'JSON'
{"current":"0.24.0","dingban":["0.24.0","0.23.0","0.22.0"],"loaded":"0.24.0",
"tags":["v0.24.0","v0.23.0","v0.22.0"],
"manifest":{"v0.24.0":"0.24.0","v0.23.0":"0.23.0","v0.22.0":"0.22.0"},
"releases":["v0.22.0"],
"commits":{"v0.22.0...v0.23.0":["定版 v0.23.0(雲端拿得到票與主線)"]}}
JSON
t 0 "最新 tag 是 v0.24.0,要建的是 v0.23.0 → 照建(前置①看 tag 的樹,不看「最新」)" \
"已建立 release「v0.23.0 — 雲端拿得到票與主線」" b4.json v0.23.0
t 0 "覆核的是 v0.23.0 這一版,不是工作樹宣稱的 0.24.0" "v0.23.0 ✅ ✅ ✅ ←" b4.json v0.23.0
not "工作樹 plugin.json 是 9.9.9 也不會被拿來比(B 群全綠就是證據,這條釘住訊息)" "9.9.9" b4.json v0.23.0
echo ""
echo "──────────────────────────────"