# -*- coding: utf-8 -*- r'''清理 EzHub 发行版里重复的附件。 为什么会重复: release_all.py 发现标签已有发行版时会"复用它", 但**没有先删同名附件**,于是每跑一次就多传一遍。 我调试时跑了几次,同一个文件就堆了 2~3 份。 这个脚本:同名附件只保留最新的一份,其余删掉。 (Gitea 的附件用 id 区分,创建时间看 created_at。) ''' from __future__ import annotations import json import sys import urllib.error import urllib.request from collections import defaultdict from pathlib import Path TOKEN = (Path.home() / ".ykxmz" / "ezhub_token").read_text( encoding="utf-8").strip() API = "https://git.261826.xyz/api/v1/repos/xmz/ykxmz" TAG = "v1.8.0" HEAD = {"Authorization": "token " + TOKEN, "User-Agent": "ykxmz-clean"} def call(url: str, method: str = "GET"): req = urllib.request.Request(url, method=method, headers=HEAD) try: with urllib.request.urlopen(req, timeout=60) as resp: text = resp.read().decode("utf-8", "replace") return resp.status, (json.loads(text) if text.strip() else {}) except urllib.error.HTTPError as exc: body = exc.read().decode("utf-8", "replace")[:200] return exc.code, body def main() -> int: dry = "--dry" in sys.argv status, release = call("%s/releases/tags/%s" % (API, TAG)) if status != 200: print(" [失败] 取发行版 HTTP %s: %s" % (status, release)) return 1 rid = release["id"] assets = release.get("assets") or [] print(" 发行版 id=%s,附件 %d 个" % (rid, len(assets))) # 按文件名分组 groups = defaultdict(list) for a in assets: groups[a.get("name") or "?"].append(a) print() print(" 分组情况:") to_delete = [] for name, items in sorted(groups.items()): # 新的排在前面(created_at 降序) items.sort(key=lambda x: x.get("created_at") or "", reverse=True) keep = items[0] print(" %-32s 共 %d 份,保留 id=%s(%s)" % (name, len(items), keep.get("id"), (keep.get("created_at") or "")[:19])) for extra in items[1:]: print(" 删 id=%s(%s)" % (extra.get("id"), (extra.get("created_at") or "")[:19])) to_delete.append(extra) if not to_delete: print() print(" ✓ 没有重复,不用清理") return 0 print() if dry: print(" 演练:会删 %d 个(去掉 --dry 真删)" % len(to_delete)) return 0 print(" 开始删除 %d 个重复附件…" % len(to_delete)) ok = 0 for asset in to_delete: status, body = call("%s/releases/%s/assets/%s" % (API, rid, asset.get("id")), method="DELETE") if status in (200, 204): print(" ✓ 删了 id=%s %s" % (asset.get("id"), asset.get("name"))) ok += 1 else: print(" ✗ id=%s HTTP %s %s" % (asset.get("id"), status, body)) print() print(" 删除 %d/%d" % (ok, len(to_delete))) # 复查 status, release = call("%s/releases/tags/%s" % (API, TAG)) assets = release.get("assets") or [] print() print(" 现在剩 %d 个附件:" % len(assets)) for a in assets: print(" %-32s %8.2f MB" % (a.get("name"), (a.get("size") or 0) / 1024 / 1024)) return 0 if __name__ == "__main__": sys.exit(main())