Files
ISEP/scripts/test-make-cloud-env.sh
T
Leo 7aef9436c9 兩台全給(youlin+geek6688),清單守則從名字改成事實(inkstone/ISEP#115 → comment 5541/5542)
leo 2026-09-01:「youlin 和 geek6688 都全給,因為這兩個都是讓 CC 操作
一個 stage 一個 test prod,不給所有的就是受阻。」覆蓋 comment 5526 的 youlin-only。

- NEEDED 從 2 把變 8 把:youlin 五把(CF token/account id/MCP owner secret/
  stage 帳密兩把)+ geek6688 兩把(出貨 token/account id)+ 機器帳號 Gitea token。
  CLOUDFLARE_ACCOUNT_ID_YOULIN 是 comment 5542 才補進 .env 的,已實查在場。
  CLOUDFLARE_API_TOKEN_leo21c 沒加(leo 只點名那兩台)。
- 新增 B9:leo21c 那把住在頂層 .env,不在 polaris/mira/.env ⇒ B8 抓不到它。
  B9 比的是**值相等**不是名字——把它改名叫 CF_TOKEN_BACKUP 照樣紅,
  而黑名單會放它過去。兩條「該紅」方向都在拋棄式副本上實跑證過。
- 密碼類兩把進清單是給人拿的;credentials-map.md「密碼類憑證的用法限制」照舊:
  AI 不得把密碼打進登入欄位。這條寫在清單旁邊,下一個要加變數的人一定會看到。

順手修掉一個不是我造成的假綠:
- hooks/tests/sdd-guard-retired.test.sh 用了 mapfile,macOS 內建 bash 3.2 沒有這個內建,
  在乾淨的 origin/main 上就是離開碼 1(實驗證過),而 TESTING.md 標著「通過 8」。
  改成 while-read(同 scripts/gitea-labels-sync.sh:56 的前例)後:通過 8 / 失敗 0。

接基底與衝突:
- rebase 到 013a987,main 那 8 筆一筆沒掉。
- TESTING.md 兩邊都新增了 A25 ⇒ main 的 A25(sdd-guard 退役)保留原編號,
  本節改為 A26;狀態表那列跟著改。
- plugin.json 取 main 那邊(version 0.18.0 未動),六個數字在併完的樹上實數:
  61/84/7/7/2/49。
- mistakes.md 兩邊各自 append,兩條都留。

值全程沒有進版控、沒有進對話(只印名字與長度)。版本號待總管定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 12:04:49 +08:00

145 lines
7.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# make-cloud-env.sh 的測試(docs/TESTING.md A14
#
# 它守什麼: 那支腳本是「雲端總管手上有哪些憑證」的唯一清單。清單漏一把,
# 雲端會在**流程末端**才發現跑不了(inkstone/ISEP#115 內文:派了工人寫完
# 清空腳本、寫完測試,到要真的跑的那一刻才知道 CF token 根本不在)。
#
# 兩段:
# A 段 —— 用假的 .env 隔離跑,證明「產物長對、缺值不會假綠、什麼都不寫進 repo」。
# 全程不碰真金鑰、不碰 ~/.claude/cloud-env。
# B 段 —— 拿這台機器真正的 .env 對帳,證明清單上每一把**這台都拿得到值**,
# 而且**沒有一把的值來自 polaris/mira/.env**leo21c 現役正式環境)。
# .env 不在(例如雲端)⇒ 印 ⏭️ SKIP,不算通過也不算失敗。
#
# 🔴 B8/B9 的判準都不是名字,是機器算得出來的事實——**換什麼名字都躲不掉**。
# B8 看「這個值是從哪個檔案拿到的」(出自 polaris/mira/.env leo21c 現役正式環境)。
# B9 看「這個值是不是就是 leo21c 那把 token」(值相等就紅,改名叫什麼都一樣)。
# 🔴 兩條是分工不是重複:`CLOUDFLARE_API_TOKEN_leo21c` 住在**頂層** .env
# 不在 polaris/mira/.env ⇒ **B8 抓不到它**,這正是 B9 存在的理由。
# 關鍵字黑名單反過來只擋得住已經被列進去的那幾個名字,新名字一律漏掉
# leo 2026-08-17 已證偽文字層封路:當日 8 次誤攔、0 次正確攔截)。
#
# 🔴 全程只印變數**名字**與值的**長度**,一個字元的值都不印。
cd "$(dirname "$0")/.." || exit 1
S=scripts/make-cloud-env.sh
PASS=0; FAIL=0; SKIP=0
ok(){ PASS=$((PASS+1)); printf ' ✅ %s\n' "$1"; }
bad(){ FAIL=$((FAIL+1)); printf ' ❌ %s\n' "$1"; }
skip(){ SKIP=$((SKIP+1)); printf ' ⏭️ SKIP %s\n' "$1"; }
chk(){ [ "$1" = "$2" ] && ok "$3$2" || bad "$3want=$1 got=$2"; }
# 清單與搜尋路徑都從腳本本身讀出來——測試不另養一份,養了就會漂。
# 註:macOS 內建的是 bash 3.2,沒有 mapfilereadarray,一律用 while read 收陣列。
NEEDED=()
while IFS= read -r l; do NEEDED+=("$l"); done < <(
sed -n '/^NEEDED=(/,/^)/p' "$S" | sed -E '1d;$d; s/[[:space:]]*#.*$//; s/[[:space:]]//g' | grep -v '^$')
[ "${#NEEDED[@]}" -gt 0 ] || { echo "🔴 從 $S 讀不出 NEEDED,測試本身壞了"; exit 1; }
echo "清單(只有名字):${NEEDED[*]}"
echo
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
FAKE="$TMP/fakeroot"; OUTD="$TMP/out"; mkdir -p "$FAKE"
echo "── A. 隔離跑(假 .env,不碰真金鑰、不碰 ~/.claude/cloud-env)──"
: > "$FAKE/.env"
for n in "${NEEDED[@]}"; do echo "$n=FAKEVALUE-$n" >> "$FAKE/.env"; done
BEFORE=$(git status --porcelain)
OUT_LINE=$(INKSTONE_ROOT="$FAKE" OUT_DIR="$OUTD" bash "$S" 2>&1)
F=$(printf '%s\n' "$OUT_LINE" | sed -n 's/^✅ 產生完成://p')
[ -f "$F" ] && ok "A1 產物產出來了" || bad "A1 產物不存在(腳本輸出:$OUT_LINE"
chk 600 "$(stat -f '%OLp' "$F" 2>/dev/null || stat -c '%a' "$F" 2>/dev/null)" "A2 產物權限"
miss=0
for n in "${NEEDED[@]}"; do grep -qx "$n=FAKEVALUE-$n" "$F" || { miss=$((miss+1)); echo " ↑ 沒出現:$n"; }; done
chk 0 "$miss" "A3 每個名字都帶著值出現在 ① 區塊"
printf '%s\n' "$OUT_LINE" | grep -q "找不到值的 0 個" \
&& ok "A4 收尾那行說「找不到值的 0 個」" || bad "A4 收尾計數不對:$OUT_LINE"
# 缺值時不准假綠:拿掉第一個,它要變成 <🔴 …> 且計數變 1
grep -v "^${NEEDED[0]}=" "$FAKE/.env" > "$FAKE/.env.tmp" && mv "$FAKE/.env.tmp" "$FAKE/.env"
OUT2=$(INKSTONE_ROOT="$FAKE" OUT_DIR="$OUTD" bash "$S" 2>&1)
F2=$(printf '%s\n' "$OUT2" | sed -n 's/^✅ 產生完成://p')
grep -q "^${NEEDED[0]}=<🔴" "$F2" && ok "A5a 缺值那行標成 <🔴 …>" || bad "A5a 缺值沒被標出來"
printf '%s\n' "$OUT2" | grep -q "找不到值的 1 個 ${NEEDED[0]}" \
&& ok "A5b 收尾點名缺的是誰" || bad "A5b 收尾沒點名:$OUT2"
if [ "$BEFORE" = "$(git status --porcelain)" ]; then ok "A6 跑完沒有任何檔案被寫進 repo"
else bad "A6 repo 裡多了東西:$(diff <(printf '%s\n' "$BEFORE") <(git status --porcelain) | tr '\n' ' ')"; fi
echo
echo "── B. 拿這台機器真正的 .env 對帳(只印長度,不印值)──"
ENVF=()
while IFS= read -r l; do ENVF+=("$l"); done < <(
sed -n '/^ENV_FILES=(/,/^)/p' "$S" | sed -E '1d;$d; s/^[[:space:]]*"//; s/"[[:space:]]*$//' | grep -v '^$')
BASE="${INKSTONE_ROOT:-$HOME/Documents/tech_projects/InkStoneCo}"
real_src(){ # $1=名字 → 印出「值的長度 來源檔」;找不到回非 0
local name="$1" f p v
for p in "${ENVF[@]}"; do
f="${p/\$BASE/$BASE}"; f="${f/\$\{BASE\}/$BASE}"
[ -f "$f" ] || continue
v=$(grep -E "^[[:space:]]*(export[[:space:]]+)?${name}=" "$f" 2>/dev/null | tail -1 \
| sed -E "s/^[[:space:]]*(export[[:space:]]+)?${name}=//" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/')
[ -n "$v" ] && { printf '%s %s' "${#v}" "$f"; return 0; }
done
return 1
}
# 只取值、不取來源,給 B9 比對用。值只活在變數裡,從不印出、也不寫檔。
_val(){ local r f; r=$(real_src "$1") || return 1; f="${r#* }"
grep -E "^[[:space:]]*(export[[:space:]]+)?$1=" "$f" | tail -1 \
| sed -E "s/^[[:space:]]*(export[[:space:]]+)?$1=//" | sed -E 's/^"(.*)"$/\1/; s/^'"'"'(.*)'"'"'$/\1/'; }
found=0
for p in "${ENVF[@]}"; do f="${p/\$BASE/$BASE}"; [ -f "$f" ] && found=1; done
if [ "$found" = 0 ]; then
skip "B7 這台機器一個 .env 都沒有(雲端就是這個狀態),對帳跳過"
skip "B8 同上"
else
gone=0; prod=0
for n in "${NEEDED[@]}"; do
if r=$(real_src "$n"); then
len="${r%% *}"; src="${r#* }"
printf ' %-38s → 找得到(長度 %s)來自 %s\n' "$n" "$len" "${src#$BASE/}"
case "$src" in */polaris/mira/.env) prod=$((prod+1)); echo " 🔴 ↑ 這是 leo21c 現役正式環境那份 .env";; esac
else
printf ' %-38s → 🔴 這台拿不到\n' "$n"; gone=$((gone+1))
fi
done
chk 0 "$gone" "B7 清單上每一把這台都拿得到值"
chk 0 "$prod" "B8 沒有一把的值來自 polaris/mira/.envleo21c 正式環境)"
# B9 —— leo 2026-09-01 的紅線:「CLOUDFLARE_API_TOKEN_leo21c 不准加」。
# 🔴 判準是**值**不是名字:拿 leo21c 那把的值,比對清單上每一把解出來的值。
# 有人把它改名叫 CF_TOKEN_BACKUP 照樣會紅,而黑名單會放它過去。
# B8 抓不到這一條(那把住在頂層 .env,不在 polaris/mira/.env)。
if leoval=$(_val CLOUDFLARE_API_TOKEN_leo21c); then
hit=0
for n in "${NEEDED[@]}"; do
v=$(_val "$n") || continue
[ "$v" = "$leoval" ] && { hit=$((hit+1)); echo " 🔴 $n 的值就是 leo21c 那把"; }
done
chk 0 "$hit" "B9 清單上沒有一把的值等於 CLOUDFLARE_API_TOKEN_leo21c"
else
skip "B9 這台找不到 CLOUDFLARE_API_TOKEN_leo21c,沒有比對基準"
fi
# B8 全綠有兩種可能:真的沒有正式憑證,或**偵測根本不會亮**。分得開才算驗過。
# 拿一個已知住在 polaris/mira/.env 的名字餵它,看它認不認得出來。
if r=$(real_src CLOUDFLARE_ACCOUNT_ID); then
case "${r#* }" in
*/polaris/mira/.env) ok "B8x 反例自驗:把 leo21c 那份 .env 的變數餵進去,偵測認得出來" ;;
*) skip "B8x 反例自驗:CLOUDFLARE_ACCOUNT_ID 這次解到 ${r#* },不是 mira 那份,這台沒得驗" ;;
esac
else
skip "B8x 反例自驗:這台找不到 CLOUDFLARE_ACCOUNT_ID,沒有現成的反例可餵"
fi
fi
echo
echo "$PASS/$((PASS+FAIL)) 通過,跳過 $SKIP 條"
[ "$FAIL" -eq 0 ]