Files
ISEP/hooks/lib/gitea_token.py
T
Claude 76adc749b0 ISEP#47: 補齊 8 支 Python 也從 env/.env 取 token,不再從 remote URL 抽
c9128 複驗指出:雲端那次只改了 6 支 shell 的 sed 寫法,漏了 8 支 Python
的 re.search(r"//[^:/]+:([^@]+)@") 寫法。remote 清乾淨後這 8 支會靜默拿到
空字串 → 401,正是票上 2026-08-13「被讀成找不到/不存在」那種壞法;雲端有
環境變數所以測不出本機(只有 .env)的壞法。

- 新增 hooks/lib/gitea_token.py:Python 端唯一 token 來源,與
  scripts/lib/gitea-token.sh 同一解析順序(env → $CLAUDE_PROJECT_DIR/.env
  → git 根的 .env),絕不從 remote URL 抽。
- 8 支改用它:scripts/ticket、debt-worklist、milestone-account、pr-verdict、
  release-manifest、isep-nag、isep-notify、hooks/lib/release_chain.py。
  release_chain 的 import 做成防禦式(旁邊沒有 gitea_token.py 也能載入,
  讀 tag/release 一律匿名),token_from_env_or_remote 保留簽章、改走新來源。
- 測試:新增 scripts/test-gitea-token.sh(16 條,Python+shell 兩份實作、
  解析順序、髒 remote 不准解出、8 支都走得過 token 步);docs/TESTING.md A44。
  beacon 測試的 mkplugin 一併複製 gitea_token.py(release_chain 現在會 import 它)。

驗過(remote 乾淨+無環境變數+.env 有 token):ticket mine/isep-nag/
isep-notify/milestone-account/pr-verdict/debt-worklist 都拿得到 token、
走得過 token 步。回歸:test-gitea-token 16/16、milestone-account 23/23、
release-manifest 21/21、debt-worklist 47/47、release-check 14/14、
release-ship 14/14、ticket-pick 84/84、ticket-triage 80/80、
ticket-repo-arg 7/7、ticket-handoff-writeback 49/49、
ticket-where-seen 17/17、isep-notify-botapi 10/10、
beacon 33/33、pr-verdict-guard 52/52。

README scripts 數在本樹實數=68(main 66→branch 67 加 git-credential-gitea.sh
→本次 68 加 test-gitea-token.sh;README 舊值 65 在 main 上就已 stale)。
hooks 仍 62,hooks-inventory.md 不動。

版本號待總管定(改到的是會被載入的 hooks/lib 與 scripts,需升版才會傳到安裝的那份)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SwZx8MkV84Cz2Mf4sDU4if
2026-09-19 03:59:51 +00:00

68 lines
2.6 KiB
Python
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.
#!/usr/bin/env python3
"""gitea_token.py — 解出 Gitea 憑證(GITEA_TOKEN_CLAUDE_CODE)的唯一 Python 來源。
為什麼存在(inkstone/ISEP#47):舊法用 `git remote -v`
`re.search(r"//[^:/]+:([^@]+)@")` 從 remote URL 抽 token ⇒ token 必須明文嵌在
URL 裡 ⇒ 任何印出 remote URL 的訊息(git-lfs 的 locksverify 那行)都會把它洩進
transcriptlog。來源統一到這裡後,remote URL 才能改乾淨。
這是 `scripts/lib/gitea-token.sh` 的 Python 對照——**順序刻意一致**,讓 shell 與
Python 兩邊解出來的是同一把(兩份必然漂移,所以只留一條路:先 env、再 .env)。
解析順序(先環境、後檔案——雲端只有 env、本機有 .env,兩邊都涵蓋):
1) 環境變數 GITEA_TOKEN_CLAUDE_CODE,再退 GITEA_TOKEN(本機 source .env 後也在)
2) $CLAUDE_PROJECT_DIR/.env 的 GITEA_TOKEN_CLAUDE_CODE=
3) git 根目錄的 .env 的 GITEA_TOKEN_CLAUDE_CODE=
讀不到 → 回 None(呼叫者自己判空,不能把 None 當成功)。
🔴 只回值、不印別的——絕不把 token 印進 log。
🔴 不從 remote URL 抽 token——那正是 ISEP#47 要根除的洩漏源。
"""
import os
import subprocess
def _from_file(path):
"""從一個 .env 檔抓 GITEA_TOKEN_CLAUDE_CODE=,去掉外圍引號。抓不到回 None。"""
try:
with open(path, encoding="utf-8", errors="replace") as f:
for line in f:
if line.startswith("GITEA_TOKEN_CLAUDE_CODE="):
v = line.split("=", 1)[1].strip()
if len(v) >= 2 and v[0] == v[-1] and v[0] in ("'", '"'):
v = v[1:-1]
return v or None
except OSError:
pass
return None
def _git_root():
"""git rev-parse --show-toplevel(跟 shell 版同一把尺)。拿不到回 None。"""
try:
out = subprocess.run(["git", "rev-parse", "--show-toplevel"],
capture_output=True, text=True, timeout=20)
root = (out.stdout or "").strip()
return root or None
except Exception:
return None
def gitea_token():
"""回 Gitea token 字串,或 None(拿不到)。順序見模組 docstring。"""
for env in ("GITEA_TOKEN_CLAUDE_CODE", "GITEA_TOKEN"):
v = os.environ.get(env)
if v:
return v
pd = os.environ.get("CLAUDE_PROJECT_DIR")
if pd:
v = _from_file(os.path.join(pd, ".env"))
if v:
return v
root = _git_root()
if root:
v = _from_file(os.path.join(root, ".env"))
if v:
return v
return None