#!/usr/bin/env bash # scripts/release-manifest 的迴歸測試(inkstone/ISEP#84) # # 兩個方向都要有證據: # A 群「該擋」——版本號在 Gitea 抓不到/清單已凍結還想加東西/退版只退一個 # B 群「該過」——版本號真的存在就凍結得起來、退版吐出整份、diff 看得出差在哪 # C 群「leo 打開看得懂」——INDEX.md 真的被重寫,而且第一份標著「現在手上的這一版」 # # 🔴 全程離線(RELEASE_MANIFEST_FIXTURE),不打 Gitea、不建任何真的 release。 # 清單寫在 mktemp 的目錄(RELEASE_MANIFEST_DIR),不碰 repo 裡的 releases/。 set -u TOOL="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/release-manifest}" TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT PASS=0; FAIL=0; N=0 FX="$TMP/fx.json" cat > "$FX" <<'JSON' {"versions": { "inkstone/Arcrun@v1.4.52": {"kind":"release","url":"u1","commit":"aaaaaaaaaaaa"}, "inkstone/Arcrun@v1.4.51": {"kind":"release","url":"u2","commit":"bbbbbbbbbbbb"}, "inkstone/arcrun-collector@v0.18.36": {"kind":"tag","url":"u3","commit":"cccccccccccc"}, "inkstone/arcrun-collector@v0.18.35": {"kind":"tag","url":"u4","commit":"dddddddddddd"} }} JSON run() { RELEASE_MANIFEST_FIXTURE="$FX" RELEASE_MANIFEST_DIR="$TMP/rel" python3 "$TOOL" "$@" 2>&1; } # t <期望 exit> <說明> <必須出現的字串(可空)> -- <參數…> t() { want="$1"; desc="$2"; must="$3"; shift 4 N=$((N+1)) out=$(run "$@"); rc=$? ok=1 [ "$rc" -eq "$want" ] || ok=0 if [ -n "$must" ] && ! printf '%s' "$out" | grep -qF "$must"; then ok=0; fi if [ "$ok" -eq 1 ]; then printf ' ✅ %s\n' "$desc"; PASS=$((PASS+1)) else printf ' ❌ %s —— 期望 exit=%s%s,實得 exit=%s\n' "$desc" "$want" \ "${must:+ 且輸出含「$must」}" "$rc" printf '%s\n' "$out" | sed -n '1,8p' | sed 's/^/ /'; FAIL=$((FAIL+1)) fi } echo "── B 群:該過 ──────────────────────────────────────────────" t 0 "① 兩個 repo 的版本都真的存在 ⇒ 凍結得起來" "已凍結(2 個 repo" -- \ freeze R-2026.08.29 --part inkstone/Arcrun@v1.4.51 --part inkstone/arcrun-collector@v0.18.35 t 0 "② 單一 repo 的交貨不必特例(清單退化成一格)" "已凍結(1 個 repo" -- \ freeze R-single --part inkstone/Arcrun@v1.4.51 t 0 "③ 再凍一份新的(這才是「新的需求進下一版」)" "已凍結(2 個 repo" -- \ freeze R-2026.08.30 --part inkstone/Arcrun@v1.4.52 --part inkstone/arcrun-collector@v0.18.36 t 0 "④ current 答得出「我現在手上是哪一版」" "R-2026.08.30" -- current t 0 "⑤ verify:每一格都還抓得到" "全部 2 格都還抓得到" -- verify R-2026.08.30 t 0 "⑥ diff 看得出兩份差在哪一格" "v1.4.51 → v1.4.52" -- diff R-2026.08.29 R-2026.08.30 t 0 "⑦ --dry-run 不寫檔" "一個檔都沒有真的寫出去" -- \ freeze R-dryrun --part inkstone/Arcrun@v1.4.52 --dry-run t 2 "⑧ 承上:--dry-run 那份真的沒被寫出去" "沒有這份清單" -- show R-dryrun echo "── A 群:該擋 ──────────────────────────────────────────────" t 2 "⑨ 版本號在 Gitea 抓不到 ⇒ 不給凍結(驗收 1)" "在 Gitea 上抓不到" -- \ freeze R-bad --part inkstone/Arcrun@v9.9.9 t 2 "⑩ 承上:整份都不寫(不會留下半份清單)" "沒有這份清單" -- show R-bad t 2 "⑪ 一格都不給 ⇒ 擋" "至少要有一格" -- freeze R-empty t 2 "⑫ 同名清單已凍結 ⇒ 不准再加東西" "新的需求進下一版" -- \ freeze R-2026.08.30 --part inkstone/Arcrun@v1.4.51 t 2 "⑬ 同一個 repo 出現兩次 ⇒ 擋" "出現兩次" -- \ freeze R-dup --part inkstone/Arcrun@v1.4.51 --part inkstone/Arcrun@v1.4.52 t 2 "⑭ 清單名亂寫 ⇒ 擋" "清單名只准" -- freeze "R 2026/08" --part inkstone/Arcrun@v1.4.51 t 2 "⑮ part 寫法不對 ⇒ 擋" "寫法是 owner/repo@版本號" -- \ freeze R-x --part Arcrun-v1.4.52 echo "── 驗收 2/3:退版 ─────────────────────────────────────────" t 0 "⑯ 退版吐出整份清單的每一格(驗收 2)" "這 2 格是一組" -- rollback R-2026.08.29 t 0 "⑰ 承上:兩個 repo 都被列進「要動的」" "arcrun-collector" -- rollback R-2026.08.29 t 2 "⑱ 試著只退其中一個 ⇒ 被擋下來(驗收 3)" "沒有「只退一個」這個選項" -- \ rollback R-2026.08.29 --only inkstone/Arcrun t 0 "⑲ 已經在目標版本的那幾格也要列出來(沒有一格被漏掉)" "不必動" -- \ rollback R-2026.08.30 echo "── 驗收 4:leo 打開那份清單看得懂 ──────────────────────────" N=$((N+1)) IDX="$TMP/rel/INDEX.md" if [ -f "$IDX" ] && grep -q "現在手上的這一版" "$IDX" \ && grep -q "R-2026.08.30" "$IDX" && grep -q "從來沒測過的組合" "$IDX"; then printf ' ✅ ⑳ INDEX.md 被重寫,標出「現在手上的這一版」與為什麼要有它\n'; PASS=$((PASS+1)) else printf ' ❌ ⑳ INDEX.md 內容不對\n'; sed -n '1,20p' "$IDX" 2>/dev/null | sed 's/^/ /' FAIL=$((FAIL+1)) fi N=$((N+1)) if head -20 "$IDX" 2>/dev/null | grep -A2 "R-2026.08.30" | grep -q "現在手上的這一版"; then printf ' ✅ ㉑ 最新那一份(不是最舊那份)才被標成「現在手上的」\n'; PASS=$((PASS+1)) else printf ' ❌ ㉑ 標錯了哪一份是現在手上的\n'; FAIL=$((FAIL+1)) fi echo echo "通過 $PASS 條,失敗 $FAIL 條(共 $N 條)" [ "$FAIL" -eq 0 ]