feat: GitHub public mirror 管線(成品櫥窗,非工作現場)
leo 2026-07-21:「應該要給的是一個適合別人用的濃縮結果,不需要有過程。
以後我還是在私有的 template 工作,但 publish 到 GitHub 就把它當貼文。」
- publish-github.sh:移植自 arcrun-rag 既有管線(乾淨歷史、憑證走 header 不進 URL)
+修一個真 bug:原版 sanitize 失敗不中止 → 會把未淨化樹 rsync 進 mirror 並推出去
- github-publish-exclude.txt:濾掉工作現場(system-dev/ .claude/ docs/ CLAUDE.md CHANGELOG.md)
保留成品(README/scripts/skills/template)
- github-publish-sanitize.py:改寫來源網址 + 發佈前驗證,殘留失效來源即中止
① git.uncle6.me(private 且即將換 CF 版,別人抓不到)
② uncle6me-web(已 suspend 的舊 GitHub 帳號——README 安裝指令原本還指著它,
別人照著跑會失敗,與 07-20 update.sh 同一顆雷)
公開樹實測:只剩 README/scripts/skills/template,安裝指令指向 youlinhsieh,零殘留。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""github-publish-sanitize.py — 對匯出樹做內容級改寫,讓公開版真的能被別人使用。
|
||||
|
||||
由 publish-github.sh 自動呼叫(步驟 3.5),參數=匯出樹的暫存目錄。
|
||||
|
||||
為什麼需要(leo 2026-07-21):
|
||||
「我不想給它 Gitea 網址,因為我們已經計劃要把 gitea 改用 CF 版本了。」
|
||||
|
||||
install.sh / update.sh 內建的抓取來源指向我們的 **private Gitea**:
|
||||
- 別人抓不到(private)→ 裝不起來
|
||||
- 且該網址即將換成 CF 版 → 公開出去的網址馬上失效
|
||||
|
||||
→ 公開版一律改指 GitHub raw。私有工作區維持 Gitea 不動,
|
||||
兩邊不必手工維護兩份(改寫發生在發佈當下)。
|
||||
"""
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
GITEA_BASE = "https://git.uncle6.me/Leo/system-dev-template/raw/branch/main"
|
||||
GITHUB_BASE = "https://raw.githubusercontent.com/youlinhsieh/system-dev-template/main"
|
||||
|
||||
# 純文字取代(來源網址)。放這裡的規則要「冪等」——重跑不會壞。
|
||||
REPLACEMENTS = [
|
||||
(GITEA_BASE, GITHUB_BASE),
|
||||
# 保險:任何殘留的 Gitea 主機名(例如註解、文件裡的說明連結)
|
||||
("https://git.uncle6.me/Leo/system-dev-template", "https://github.com/youlinhsieh/system-dev-template"),
|
||||
# 🔴 舊 GitHub 帳號 uncle6me-web **已被 suspend**:README 的安裝指令仍指向它
|
||||
# → 別人照著跑會抓不到(2026-07-20 已在 update.sh 撞過同一顆雷)。
|
||||
("raw.githubusercontent.com/uncle6me-web/", "raw.githubusercontent.com/youlinhsieh/"),
|
||||
("github.com/uncle6me-web/", "github.com/youlinhsieh/"),
|
||||
# 文件註解裡拿舊帳號當範例 → 對外改通用佔位符(別人看到我們的帳號名沒意義)
|
||||
("(例:uncle6me-web)", "(例:your-github-account)"),
|
||||
("(e.g. uncle6me-web)", "(e.g. your-github-account)"),
|
||||
]
|
||||
|
||||
TEXT_SUFFIXES = {".sh", ".md", ".json", ".py", ".yaml", ".yml", ".txt"}
|
||||
|
||||
|
||||
def main(root_arg: str) -> int:
|
||||
root = pathlib.Path(root_arg)
|
||||
if not root.is_dir():
|
||||
print(f"❌ sanitize:找不到匯出樹 {root}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
changed = []
|
||||
for path in root.rglob("*"):
|
||||
if not path.is_file() or path.suffix not in TEXT_SUFFIXES:
|
||||
continue
|
||||
try:
|
||||
original = path.read_text(encoding="utf-8")
|
||||
except (UnicodeDecodeError, OSError):
|
||||
continue
|
||||
|
||||
text = original
|
||||
for old, new in REPLACEMENTS:
|
||||
text = text.replace(old, new)
|
||||
|
||||
if text != original:
|
||||
path.write_text(text, encoding="utf-8")
|
||||
changed.append(str(path.relative_to(root)))
|
||||
|
||||
if changed:
|
||||
print(f"🧼 sanitize:改寫來源網址 → GitHub({len(changed)} 檔)")
|
||||
for c in changed:
|
||||
print(f" {c}")
|
||||
else:
|
||||
print("🧼 sanitize:無需改寫")
|
||||
|
||||
# 驗證:公開樹不得殘留「別人抓不到的來源」——漏了就是別人裝不起來。
|
||||
# ① git.uncle6.me = 我們的 private Gitea(且即將換 CF 版)
|
||||
# ② uncle6me-web = 已被 suspend 的舊 GitHub 帳號
|
||||
BAD_HOSTS = ("git.uncle6.me", "uncle6me-web")
|
||||
leaked = []
|
||||
for path in root.rglob("*"):
|
||||
if not path.is_file() or path.suffix not in TEXT_SUFFIXES:
|
||||
continue
|
||||
try:
|
||||
content = path.read_text(encoding="utf-8")
|
||||
if any(bad in content for bad in BAD_HOSTS):
|
||||
leaked.append(str(path.relative_to(root)))
|
||||
except (UnicodeDecodeError, OSError):
|
||||
continue
|
||||
|
||||
if leaked:
|
||||
print("❌ sanitize:公開樹仍殘留失效來源(Gitea/suspend 帳號),中止發佈:", file=sys.stderr)
|
||||
for f in leaked:
|
||||
print(f" {f}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1] if len(sys.argv) > 1 else "."))
|
||||
Reference in New Issue
Block a user