# -*- coding: utf-8 -*- r'''撤掉 Gitee 发行版介绍里泄漏的公网 IP。 == 用户报的 == "那个发行版的介绍里,服务器公网 IP 地址暴露出去了" 来源:发布脚本(gitee_release.py / release_all.py)把 _build/CHANGELOG.md 当"更新说明"传给 Gitee 建发行版, 而 CHANGELOG 的 2.1.3 那节里写了 <源站IP>。 工作区那边已经清了(_build/scrub_server_ip.py), 但**已经发出去的发行版介绍还在 Gitee 上**,得用 API 改回去。 == 接口 == PATCH /api/v5/repos/{owner}/{repo}/releases/{id} 参数:access_token + body(新的发行版介绍) == 顺带检查所有发行版 == 不只 v2.1.3 —— 从 2.1.0 起我就在"把 CHANGELOG 当更新说明", 而 2.1.0/2.1.1 那几节也提到过服务器。全部扫一遍、有 IP 的都改掉。 ''' from __future__ import annotations import json import sys import urllib.parse import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent.parent # ⚠ 这个脚本要"在所有发行版介绍里找这个 IP",所以它必须知道 IP。 # 但**不能在源码里写死** —— 写了就等于把 IP 又发到公开仓库里, # 正是这个脚本要防的事。 # 从 lssh 取(那里会读环境变量 YKXMZ_LS_HOST 或 # _build/.lightsail_host,后者已 gitignore)。 sys.path.insert(0, str(Path(__file__).resolve().parent)) from lssh import HOST as IP, HOST as LS_HOST # noqa: E402 DOMAIN = "yk.xxmxxs.top" OWNER_REPO = "xmz7891/ykxmz" def api(url: str, data: bytes = None, method: str = None) -> tuple: req = urllib.request.Request(url, data=data, method=method) req.add_header("User-Agent", "ykxmz-release-fix") if data is not None: req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req, timeout=60) as r: return r.status, json.loads(r.read().decode("utf-8")) except urllib.error.HTTPError as exc: return exc.code, exc.read().decode("utf-8", "replace")[:300] def main() -> int: token_file = ROOT / "_build" / ".gitee_token" if not token_file.is_file(): print(" ✗ 找不到 _build/.gitee_token") return 1 token = token_file.read_text(encoding="utf-8").strip() print("=" * 62) print(" 撤掉 Gitee 发行版介绍里的公网 IP") print("=" * 62) print() print(" ── 1. 列出所有发行版 ──") code, data = api( "https://gitee.com/api/v5/repos/%s/releases" "?access_token=%s&per_page=60" % (OWNER_REPO, token)) if code != 200: print(" ✗ 列不出来: %s" % data) return 1 print(" 共 %d 个发行版" % len(data)) print() print(" ── 2. 逐个检查 body,有 IP 的改掉 ──") fixed = 0 for rel in data: tag = rel.get("tag_name", "?") rid = rel.get("id") body = rel.get("body") or "" if IP not in body: print(" %-10s 干净" % tag) continue n = body.count(IP) new_body = body.replace(IP, DOMAIN) print(" %-10s ⚠ 有 %d 处 -> 改掉" % (tag, n)) # ⚠ 两个坑: # # 1. 必须用 **JSON**。第一版 urlencode 拼了 data,但 api() # 见 data 非空就声明 Content-Type: application/json —— # 内容和声明不一致,Gitee 回 400(一页 HTML 错误页)。 # # 2. **tag_name 和 name 必须一起传**,即使不改它们。 # 漏了会报 {"messages":["tag_name is missing", # "name is missing"]}。 # (PATCH 语义上应该允许只改 body,但 Gitee 的实现要求 # 把标识字段带上 —— 那就带上。) payload = json.dumps({ "access_token": token, "tag_name": tag, "name": rel.get("name") or tag, "body": new_body, }).encode("utf-8") code2, resp = api( "https://gitee.com/api/v5/repos/%s/releases/%s" % (OWNER_REPO, rid), data=payload, method="PATCH") if code2 == 200: print(" ✓ 已更新") fixed += 1 else: print(" ✗ 更新失败 (%s): %s" % (code2, resp)) print() print(" ── 3. 复查 ──") code, data = api( "https://gitee.com/api/v5/repos/%s/releases" "?access_token=%s&per_page=60" % (OWNER_REPO, token)) still = [r.get("tag_name") for r in data if IP in (r.get("body") or "")] if still: print(" ⚠ 还有: %s" % ", ".join(still)) else: print(" ✅ 所有发行版介绍里都没有这个 IP 了") print() print(" 改了 %d 个发行版" % fixed) return 0 if __name__ == "__main__": sys.exit(main())