ISEP#47: source Gitea token from env/.env, not remote URL (9 scripts + credential helper)

- 6 hooks + gitea-labels-sync/wiki-panorama/gitea-bootstrap 全改用 gitea_token()
- wiki-panorama clone/fetch 改乾淨 URL + credential helper
- gitea-bootstrap 冪等清 remote URL + 設 credential.helper
- 驗證:bootstrap/labels-sync API 認證通過、clone/push 經 helper 無 token 洩漏

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kxf68FcYGqiuLAPiKKU2U3
This commit is contained in:
2026-09-18 12:36:30 +00:00
parent c7890335cc
commit daa84b583f
9 changed files with 69 additions and 58 deletions
+20 -5
View File
@@ -16,14 +16,29 @@ set -eu
REPO=""
[ "${1:-}" = "-r" ] && { REPO="${2:?-r 後面要接 owner/repo}"; }
REMOTE=$(git remote get-url gitea 2>/dev/null) || {
echo "✗ 當前目錄沒有名為 gitea 的 remote。請 cd 到目標 repo,或先加 remote。" >&2; exit 1; }
# 取當前 repo 的 remote(雲端叫 origin、本機慣例叫 gitea
REMOTE_NAME=$(git remote get-url gitea >/dev/null 2>&1 && echo gitea || (git remote get-url origin >/dev/null 2>&1 && echo origin))
[ -n "$REMOTE_NAME" ] || { echo "✗ 當前目錄沒有 gitea/origin remote。請 cd 到目標 repo,或先加 remote。" >&2; exit 1; }
REMOTE=$(git remote get-url "$REMOTE_NAME" 2>/dev/null)
# 去掉網址裡可能嵌著的憑證 → 乾淨 URLinkstone/ISEP#47token 不進 remote URL
REMOTE=$(printf '%s' "$REMOTE" | sed -E 's#//[^/@]*@#//#')
TOKEN=$(printf '%s' "$REMOTE" | sed -E 's|.*//[^:]+:([^@]+)@.*|\1|')
[ "$TOKEN" = "$REMOTE" ] && { echo "✗ gitea remote URL 裡沒有 token,無法取得認證。" >&2; exit 1; }
HOST=$(printf '%s' "$REMOTE" | sed -E 's|.*@([^/]+)/.*|\1|')
# token 來源=env.env(不從 remote URL 抽——那會逼 token 明文嵌在 URL 裡 → 洩進 log)
_GT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
[ -f "$_GT_DIR/lib/gitea-token.sh" ] && . "$_GT_DIR/lib/gitea-token.sh"
TOKEN=""; command -v gitea_token >/dev/null 2>&1 && TOKEN=$(gitea_token || true)
[ -n "$TOKEN" ] || { echo "✗ 取不到 Gitea token(設 GITEA_TOKEN_CLAUDE_CODE,或在 .env 放 GITEA_TOKEN_CLAUDE_CODE=)。" >&2; exit 1; }
HOST=$(printf '%s' "$REMOTE" | sed -E 's|.*://([^/]+)/.*|\1|')
[ -n "$REPO" ] || REPO=$(printf '%s' "$REMOTE" | sed -E 's|.*://[^/]+/(.+)\.git$|\1|')
# 設定乾淨 remote URL + credential helper(冪等):URL 不含 token,認證改由 helper 從 env.env 供
# ⇒ git 把 remote URL 印出來(git-lfs 的 locksverify 那行)也不會洩 tokeninkstone/ISEP#47)。
if [ -f "$_GT_DIR/git-credential-gitea.sh" ]; then
CUR=$(git remote get-url "$REMOTE_NAME" 2>/dev/null || true)
case "$CUR" in *@*) git remote set-url "$REMOTE_NAME" "$REMOTE" && echo " ✓ remote $REMOTE_NAME 已清乾淨(移除嵌入的 token" ;; esac
git config credential.helper "$_GT_DIR/git-credential-gitea.sh"
fi
echo "→ repo: $REPO host: $HOST"
# 狀態機(互斥 scope label)。順序=出貨流程:
+5 -6
View File
@@ -30,16 +30,15 @@ if [[ ! -f "$LABELS_YAML" ]]; then
exit 1
fi
# --- token跟 InkStoneCo 頂層 gitea remote 拿同一把(不落地檔案、不印出來 ---
# --- token來源=env.envinkstone/ISEP#47),不再從 remote URL 抽(那會逼 token 明文嵌在 URL 裡 ---
TOKEN="${GITEA_TOKEN:-}"
if [[ -z "$TOKEN" ]]; then
INKSTONE_TOP="${INKSTONE_TOP:-$HOME/Documents/tech_projects/InkStoneCo}"
if [[ -d "$INKSTONE_TOP/.git" ]]; then
TOKEN="$(git -C "$INKSTONE_TOP" remote get-url gitea 2>/dev/null | sed -E 's|.*//[^:]+:([^@]+)@.*|\1|')"
fi
_GT_LIB="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)/lib/gitea-token.sh"
[[ -f "$_GT_LIB" ]] && . "$_GT_LIB"
command -v gitea_token >/dev/null 2>&1 && TOKEN="$(gitea_token || true)"
fi
if [[ -z "$TOKEN" ]]; then
echo "拿不到 Gitea token——設 GITEA_TOKEN 環境變數,或確認 $INKSTONE_TOP 的 gitea remote 存在" >&2
echo "拿不到 Gitea token——設 GITEA_TOKEN_CLAUDE_CODE 環境變數,或在 .env/頂層 .env 放 GITEA_TOKEN_CLAUDE_CODE=" >&2
exit 1
fi
+14 -8
View File
@@ -61,15 +61,21 @@ done
[ -f "$ROSTER" ] || { echo "找不到 roster$ROSTER" >&2; exit 1; }
# ── Gitea base含憑證,**絕不可印出來**)────────────────────────────────
REMOTE=$(git -C "$ROOT" remote get-url gitea 2>/dev/null || true)
[ -n "$REMOTE" ] || { echo "本 repo 沒有 gitea remote,無法取 repo。" >&2; exit 1; }
# ── Gitea base乾淨網址;token 來源=env.env,不從 URL 抽——inkstone/ISEP#47──────────
_GT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
[ -f "$_GT_DIR/lib/gitea-token.sh" ] && . "$_GT_DIR/lib/gitea-token.sh"
REMOTE=$(git -C "$ROOT" remote get-url gitea 2>/dev/null || git -C "$ROOT" remote get-url origin 2>/dev/null || true)
[ -n "$REMOTE" ] || { echo "本 repo 沒有 gitea/origin remote,無法取 repo。" >&2; exit 1; }
# 去掉網址裡可能還嵌著的舊憑證(遷移期相容)→ 乾淨 baseclone/fetch 靠 credential helper 認證
REMOTE=$(printf '%s' "$REMOTE" | sed -E 's#//[^/@]*@#//#')
BASE=${REMOTE%/InkStoneCo.git}
BASE=${BASE%/InkStoneCo}
SELF=$(basename "$REMOTE" .git)
SECRET=$(printf '%s' "$REMOTE" | sed -nE 's|.*//[^:]+:([^@]+)@.*|\1|p')
# 任何要外流的字串都先過這關(錯誤訊息可能夾帶 clone URL)
# scrub 用的 SECRETtoken 從 env.env 取(不從 URL 抽),用來遮掉萬一夾帶在錯誤訊息裡的 token
SECRET=""; command -v gitea_token >/dev/null 2>&1 && SECRET=$(gitea_token || true)
scrub() { if [ -n "$SECRET" ]; then sed "s|$SECRET|***|g"; else cat; fi; }
# clone/fetch 的認證:乾淨 URL + credential helpertoken 不進 URL、不洩進 log
GIT_CRED=""; [ -f "$_GT_DIR/git-credential-gitea.sh" ] && GIT_CRED="-c credential.helper=$_GT_DIR/git-credential-gitea.sh"
mkdir -p "$CACHE"
# 自己這個 repo 一律讀工作副本 ⇒ 快取裡若留著一份舊的自己,是誤導來源(會被人拿去讀)
@@ -95,7 +101,7 @@ for repo in $REPOS; do
if [ -d "$dst/.git" ]; then
if [ "$DO_FETCH" = 1 ]; then
br=$(git -C "$dst" symbolic-ref --short HEAD 2>/dev/null || echo main)
git -C "$dst" fetch --quiet origin "$br" 2>&1 | scrub >&2 || true
git $GIT_CRED -C "$dst" fetch --quiet origin "$br" 2>&1 | scrub >&2 || true
git -C "$dst" reset --quiet --hard FETCH_HEAD 2>/dev/null || true
fi
else
@@ -104,7 +110,7 @@ for repo in $REPOS; do
fi
# blobless + sparse:只下載 wiki 目錄的內容,但保留完整 commit 歷史
#(要歷史才算得出「每個檔最後更新是哪天」——淺 clone 會讓所有檔同一天)
if ! err=$(git clone --quiet --filter=blob:none --sparse "$BASE/$repo.git" "$dst" 2>&1 | scrub); then
if ! err=$(git $GIT_CRED clone --quiet --filter=blob:none --sparse "$BASE/$repo.git" "$dst" 2>&1 | scrub); then
printf '%s\tclone-failed\t%s\n' "$repo" "$(printf '%s' "$err" | tr '\n' ' ')" >> "$STATUS_TSV"
rm -rf "$dst"; continue
fi
@@ -312,7 +318,7 @@ if gap > 0:
f"Gitea `Leo/*` 在 {ASOF} 實查是 **{TOTAL} 個**,名單 {len(repos)} 個 ⇒ **還有 {gap} 個沒被點名**。")
w(" 補法(人發起,一次呼叫,不排程)——拿到完整清單、把缺的名字加進名單再重跑:")
w(" ```")
w(" TOKEN=$(git remote get-url gitea | sed -E 's|.*//[^:]+:([^@]+)@.*|\\1|')")
w(" TOKEN=$GITEA_TOKEN_CLAUDE_CODE # 來源=env.env,不從 remote URL 抽(inkstone/ISEP#47")
w(" curl -s -H \"Authorization: token $TOKEN\" \\")
w(" 'https://git.uncle6.me/api/v1/orgs/Leo/repos?limit=100' | python3 -c \\")
w(" 'import sys,json;[print(r[\"name\"]) for r in json.load(sys.stdin)]'")